Esempio n. 1
0
func (o *user) patchPassword(patch *jsonpatch.Patch) *he.Response {
	// this is in the running for the grossest code I've ever
	// written, but I think it's the best way to do it --lukeshu
	type patchop struct {
		Op    string `json:"op"`
		Path  string `json:"path"`
		Value string `json:"value"`
	}
	str, err := json.Marshal(patch)
	if err != nil {
		panic(err)
	}
	var ops []patchop
	err = json.Unmarshal(str, &ops)
	if err != nil {
		return nil
	}
	outOps := make([]patchop, 0, len(ops))
	checkedpass := false
	for _, op := range ops {
		if op.Path == "/password" {
			switch op.Op {
			case "test":
				if !o.backend().CheckPassword(op.Value) {
					ret := rfc7231.StatusConflict(he.NetPrintf("old password didn't match"))
					return &ret
				}
				checkedpass = true
			case "replace":
				if !checkedpass {
					ret := rfc7231.StatusUnsupportedMediaType(he.NetPrintf("you must submit and old password (using 'test') before setting a new one"))
					return &ret
				}
				if o.backend().CheckPassword(op.Value) {
					ret := rfc7231.StatusConflict(he.NetPrintf("that new password is the same as the old one"))
					return &ret
				}
				o.backend().SetPassword(op.Value)
			default:
				ret := rfc7231.StatusUnsupportedMediaType(he.NetPrintf("you may only 'set' or 'replace' the password"))
				return &ret
			}
		} else {
			outOps = append(outOps, op)
		}
	}
	str, err = json.Marshal(outOps)
	if err != nil {
		panic(err)
	}
	var out jsonpatch.JSONPatch
	err = json.Unmarshal(str, &out)
	if err != nil {
		panic(out)
	}
	*patch = out
	return nil
}
Esempio n. 2
0
func MiddlewareDatabase(config *periwinkle.Cfg) he.Middleware {
	return he.Middleware{
		Outside: func(req he.Request, handle func(he.Request) he.Response) he.Response {
			var res he.Response
			conflict := config.DB.Do(func(transaction *periwinkle.Tx) {
				req.Things["db"] = transaction
				res = handle(req)
			})
			if conflict != nil {
				res = rfc7231.StatusConflict(he.NetStringer{conflict})
			}
			return res
		},
	}
}
func (usr *userSubscriptions) Methods() map[string]func(he.Request) he.Response {
	return map[string]func(he.Request) he.Response{
		"GET": func(req he.Request) he.Response {
			db := req.Things["db"].(*periwinkle.Tx)
			usr.groupID = req.URL.Query().Get("group_id")
			usr.values = usr.backend().GetFrontEndSubscriptions(db)
			return rfc7231.StatusOK(usr)
		},
		"POST": func(req he.Request) he.Response {
			db := req.Things["db"].(*periwinkle.Tx)
			sess := req.Things["session"].(*backend.Session)
			type postfmt struct {
				GroupID string `json:"group_id"`
				Medium  string `json:"medium,omitempty"`
				Address string `json:"address,omitempty"`
			}
			var entity postfmt
			httperr := safeDecodeJSON(req.Entity, &entity)
			if httperr != nil {
				return *httperr
			}
			entity.GroupID = strings.ToLower(entity.GroupID)

			var address *backend.UserAddress
			if entity.Medium == "" && entity.Address == "" {
				address = &usr.Addresses[0]
				for _, addr := range usr.Addresses {
					if addr.SortOrder < address.SortOrder {
						address = &addr
					}
				}
			} else {
				for _, addr := range usr.Addresses {
					if addr.Medium == entity.Medium && addr.Address == entity.Address {
						address = &addr
						break
					}
				}
			}
			if address == nil {
				return rfc7231.StatusConflict(he.NetPrintf("You don't have that address"))
			}
			backend.NewSubscription(db, address.ID, entity.GroupID, sess != nil && sess.UserID == usr.ID)
			return rfc7231.StatusCreated(usr, entity.GroupID+":"+address.Medium+":"+address.Address, req)
		},
	}
}
Esempio n. 4
0
func newDirUsers() dirUsers {
	r := dirUsers{}
	r.methods = map[string]func(he.Request) he.Response{
		"POST": func(req he.Request) he.Response {
			db := req.Things["db"].(*periwinkle.Tx)
			type postfmt struct {
				Username             string `json:"username"`
				Email                string `json:"email"`
				Password             string `json:"password"`
				PasswordVerification string `json:"password_verification,omitempty"`
			}
			var entity postfmt
			httperr := safeDecodeJSON(req.Entity, &entity)
			if httperr != nil {
				return *httperr
			}

			if entity.Username == "" || entity.Email == "" || entity.Password == "" {
				return rfc7231.StatusUnsupportedMediaType(he.NetPrintf("username, email, and password can't be emtpy"))
			}

			if entity.PasswordVerification != "" {
				if entity.Password != entity.PasswordVerification {
					// Passwords don't match
					return rfc7231.StatusConflict(he.NetPrintf("password and password_verification don't match"))
				}
			}

			usr := backend.NewUser(db, entity.Username, entity.Password, entity.Email)
			backend.NewUserAddress(db, usr.ID, "noop", backend.RandomString(20), true)
			backend.NewUserAddress(db, usr.ID, "admin", backend.RandomString(20), true)
			req.Things["user"] = usr
			return rfc7231.StatusCreated(r, usr.ID, req)
		},
	}
	return r
}
Esempio n. 5
0
func (o *group) Methods() map[string]func(he.Request) he.Response {
	return map[string]func(he.Request) he.Response{
		"GET": func(req he.Request) he.Response {
			var enum Enumerategroup
			enum = EnumerateGroup(o)
			return rfc7231.StatusOK(he.NetJSON{Data: enum})
		},
		"PUT": func(req he.Request) he.Response {
			db := req.Things["db"].(*periwinkle.Tx)

			var newGroup group
			httperr := safeDecodeJSON(req.Entity, &newGroup)
			if httperr != nil {
				return *httperr
			}
			if o.ID != newGroup.ID {
				return rfc7231.StatusConflict(he.NetPrintf("Cannot change group id"))
			}
			*o = newGroup
			o.backend().Save(db)
			return rfc7231.StatusOK(o)
		},
		"PATCH": func(req he.Request) he.Response {
			db := req.Things["db"].(*periwinkle.Tx)
			sess := req.Things["session"].(*backend.Session)
			subscribed := backend.IsSubscribed(db, sess.UserID, *o.backend())
			if !backend.IsAdmin(db, sess.UserID, *o.backend()) {
				if o.JoinPublic == 1 {
					if subscribed == 0 {
						return rfc7231.StatusForbidden(he.NetPrintf("Unauthorized user"))
					}
					if o.JoinConfirmed == 1 && subscribed == 1 {
						return rfc7231.StatusForbidden(he.NetPrintf("Unauthorized user"))
					}
					if o.JoinMember == 1 {
						return rfc7231.StatusForbidden(he.NetPrintf("Unauthorized user"))
					}
				}
			}
			enum := EnumerateGroup(o)
			var newGroup Enumerategroup
			patch, ok := req.Entity.(jsonpatch.Patch)
			if !ok {
				return rfc7231.StatusUnsupportedMediaType(he.NetPrintf("PATCH request must have a patch media type"))
			}
			err := patch.Apply(enum, &newGroup)
			if err != nil {
				return rfc7231.StatusConflict(he.NetPrintf("%v", err))
			}
			if o.ID != newGroup.Groupname {
				return rfc7231.StatusConflict(he.NetPrintf("Cannot change group id"))
			}

			*o = RenumerateGroup(newGroup)
			o.backend().Save(db)
			return rfc7231.StatusOK(o)
		},
		"DELETE": func(req he.Request) he.Response {
			db := req.Things["db"].(*periwinkle.Tx)
			sess := req.Things["session"].(*backend.Session)
			if !backend.IsAdmin(db, sess.UserID, *o.backend()) {
				return rfc7231.StatusForbidden(he.NetPrintf("Unauthorized user"))
			}
			o.backend().Delete(db)
			return rfc7231.StatusNoContent()
		},
	}
}
Esempio n. 6
0
func newDirGroups() dirGroups {
	r := dirGroups{}
	r.methods = map[string]func(he.Request) he.Response{
		"GET": func(req he.Request) he.Response {
			db := req.Things["db"].(*periwinkle.Tx)
			sess := req.Things["session"].(*backend.Session)
			var groups []backend.Group
			type getfmt struct {
				visibility string
			}
			var entity getfmt
			httperr := safeDecodeJSON(req.Entity, &entity)
			if httperr != nil {
				entity.visibility = "subscribed"
			}
			if sess == nil {
				groups = []backend.Group{}
			} else if entity.visibility == "subscribed" {
				groups = backend.GetGroupsByMember(db, *backend.GetUserByID(db, sess.UserID))
			} else {
				//groups = GetAllGroups(db)
				groups = backend.GetPublicAndSubscribedGroups(db, *backend.GetUserByID(db, sess.UserID))
			}
			type EnumerateGroup struct {
				Groupname     string                 `json:"groupname"`
				Post          map[string]string      `json:"post"`
				Join          map[string]string      `json:"join"`
				Read          map[string]string      `json:"read"`
				Existence     map[string]string      `json:"existence"`
				Subscriptions []backend.Subscription `json:"subscriptions"`
			}
			data := make([]EnumerateGroup, len(groups))

			for i, grp := range groups {
				var enum EnumerateGroup
				enum.Groupname = grp.ID
				exist := [...]int{grp.ExistencePublic, grp.ExistenceConfirmed}
				enum.Existence = backend.ReadExist(exist)
				read := [...]int{grp.ReadPublic, grp.ReadConfirmed}
				enum.Read = backend.ReadExist(read)
				post := [...]int{grp.PostPublic, grp.PostConfirmed, grp.PostMember}
				enum.Post = backend.PostJoin(post)
				join := [...]int{grp.JoinPublic, grp.JoinConfirmed, grp.JoinMember}
				enum.Join = backend.PostJoin(join)
				enum.Subscriptions = grp.Subscriptions
				data[i] = enum
			}
			return rfc7231.StatusOK(he.NetJSON{Data: data})
		},
		"POST": func(req he.Request) he.Response {
			db := req.Things["db"].(*periwinkle.Tx)
			type Response1 struct {
				Groupname string            `json:"groupname"`
				Post      map[string]string `json:"post"`
				Join      map[string]string `json:"join"`
				Read      map[string]string `json:"read"`
				Existence map[string]string `json:"existence"`
			}
			var entity Response1
			httperr := safeDecodeJSON(req.Entity, &entity)
			if httperr != nil {
				return *httperr
			}

			if entity.Groupname == "" {
				return rfc7231.StatusUnsupportedMediaType(he.NetPrintf("groupname can't be emtpy"))
			}

			grp := backend.NewGroup(
				db,
				entity.Groupname,
				backend.Reverse(entity.Existence),
				backend.Reverse(entity.Read),
				backend.Reverse(entity.Post),
				backend.Reverse(entity.Join),
			)
			sess := req.Things["session"].(*backend.Session)
			address := backend.GetAddressesByUserAndMedium(db, sess.UserID, "noop")[0]
			backend.NewSubscription(db, address.ID, grp.ID, true)
			if grp == nil {
				return rfc7231.StatusConflict(he.NetPrintf("a group with that name already exists"))
			} else {
				return rfc7231.StatusCreated(r, grp.ID, req)
			}
		},
	}
	return r
}
Esempio n. 7
0
func (usr *user) Methods() map[string]func(he.Request) he.Response {
	return map[string]func(he.Request) he.Response{
		"GET": func(req he.Request) he.Response {
			var addresses []backend.UserAddress
			for _, addr := range usr.Addresses {
				if addr.Medium != "noop" && addr.Medium != "admin" {
					addresses = append(addresses, addr)
				}
			}
			usr.Addresses = addresses
			return rfc7231.StatusOK(usr)
		},
		"PUT": func(req he.Request) he.Response {
			db := req.Things["db"].(*periwinkle.Tx)
			sess := req.Things["session"].(*backend.Session)
			if sess.UserID != usr.ID {
				return rfc7231.StatusForbidden(he.NetPrintf("Unauthorized user"))
			}
			var newUser user
			httperr := safeDecodeJSON(req.Entity, &newUser)
			if httperr != nil {
				return *httperr
			}
			if usr.ID != newUser.ID {
				return rfc7231.StatusConflict(he.NetPrintf("Cannot change user id"))
			}
			// TODO: this won't play nice with the
			// password hash (because it's private), or
			// with addresses (because the (private) IDs
			// need to be made to match up)
			*usr = newUser
			usr.backend().Save(db)
			return rfc7231.StatusOK(usr)
		},
		"PATCH": func(req he.Request) he.Response {
			db := req.Things["db"].(*periwinkle.Tx)
			sess := req.Things["session"].(*backend.Session)
			if sess.UserID != usr.ID {
				return rfc7231.StatusForbidden(he.NetPrintf("Unauthorized user"))
			}
			patch, ok := req.Entity.(jsonpatch.Patch)
			if !ok {
				return rfc7231.StatusUnsupportedMediaType(he.NetPrintf("PATCH request must have a patch media type"))
			}
			httperr := usr.patchPassword(&patch)
			if httperr != nil {
				return *httperr
			}
			var newUser user
			err := patch.Apply(usr, &newUser)
			if err != nil {
				return rfc7231.StatusConflict(he.ErrorToNetEntity(409, err))
			}
			if usr.ID != newUser.ID {
				return rfc7231.StatusConflict(he.NetPrintf("Cannot change user id"))
			}
			if newUser.PwHash == nil || len(newUser.PwHash) == 0 {
				newUser.PwHash = usr.PwHash
			}
			*usr = newUser
			usr.backend().Save(db)
			return rfc7231.StatusOK(usr)
		},
		"DELETE": func(req he.Request) he.Response {
			db := req.Things["db"].(*periwinkle.Tx)
			usr.backend().Delete(db)
			return rfc7231.StatusNoContent()
		},
	}
}