Improve role membership management through REST API
This commit is contained in:
@@ -820,7 +820,7 @@
|
||||
{
|
||||
"name": "read",
|
||||
"method": "GET",
|
||||
"title": "Read user details and memberships",
|
||||
"title": "Read user details",
|
||||
"path": "/{userID}",
|
||||
"parameters": {
|
||||
"path": [
|
||||
@@ -905,6 +905,66 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "membershipList",
|
||||
"method": "GET",
|
||||
"title": "Add member to a role",
|
||||
"path": "/{userID}/membership",
|
||||
"parameters": {
|
||||
"path": [
|
||||
{
|
||||
"type": "uint64",
|
||||
"name": "userID",
|
||||
"required": true,
|
||||
"title": "User ID"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "membershipAdd",
|
||||
"method": "POST",
|
||||
"title": "Add role to a user",
|
||||
"path": "/{userID}/membership/{roleID}",
|
||||
"parameters": {
|
||||
"path": [
|
||||
{
|
||||
"type": "uint64",
|
||||
"name": "roleID",
|
||||
"required": true,
|
||||
"title": "Role ID"
|
||||
},
|
||||
{
|
||||
"type": "uint64",
|
||||
"name": "userID",
|
||||
"required": true,
|
||||
"title": "User ID"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "membershipRemove",
|
||||
"method": "DELETE",
|
||||
"title": "Remove role from a user",
|
||||
"path": "/{userID}/membership/{roleID}",
|
||||
"parameters": {
|
||||
"path": [
|
||||
{
|
||||
"type": "uint64",
|
||||
"name": "roleID",
|
||||
"required": true,
|
||||
"title": "Role ID"
|
||||
},
|
||||
{
|
||||
"type": "uint64",
|
||||
"name": "userID",
|
||||
"required": true,
|
||||
"title": "User ID"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -159,7 +159,7 @@
|
||||
{
|
||||
"Name": "read",
|
||||
"Method": "GET",
|
||||
"Title": "Read user details and memberships",
|
||||
"Title": "Read user details",
|
||||
"Path": "/{userID}",
|
||||
"Parameters": {
|
||||
"path": [
|
||||
@@ -244,6 +244,66 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "membershipList",
|
||||
"Method": "GET",
|
||||
"Title": "Add member to a role",
|
||||
"Path": "/{userID}/membership",
|
||||
"Parameters": {
|
||||
"path": [
|
||||
{
|
||||
"name": "userID",
|
||||
"required": true,
|
||||
"title": "User ID",
|
||||
"type": "uint64"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "membershipAdd",
|
||||
"Method": "POST",
|
||||
"Title": "Add role to a user",
|
||||
"Path": "/{userID}/membership/{roleID}",
|
||||
"Parameters": {
|
||||
"path": [
|
||||
{
|
||||
"name": "roleID",
|
||||
"required": true,
|
||||
"title": "Role ID",
|
||||
"type": "uint64"
|
||||
},
|
||||
{
|
||||
"name": "userID",
|
||||
"required": true,
|
||||
"title": "User ID",
|
||||
"type": "uint64"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "membershipRemove",
|
||||
"Method": "DELETE",
|
||||
"Title": "Remove role from a user",
|
||||
"Path": "/{userID}/membership/{roleID}",
|
||||
"Parameters": {
|
||||
"path": [
|
||||
{
|
||||
"name": "roleID",
|
||||
"required": true,
|
||||
"title": "Role ID",
|
||||
"type": "uint64"
|
||||
},
|
||||
{
|
||||
"name": "userID",
|
||||
"required": true,
|
||||
"title": "User ID",
|
||||
"type": "uint64"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -730,11 +730,14 @@ An organisation may have many roles. Roles may have many channels available. Acc
|
||||
| `GET` | `/users/` | Search users (Directory) |
|
||||
| `POST` | `/users/` | Create user |
|
||||
| `PUT` | `/users/{userID}` | Update user details |
|
||||
| `GET` | `/users/{userID}` | Read user details and memberships |
|
||||
| `GET` | `/users/{userID}` | Read user details |
|
||||
| `DELETE` | `/users/{userID}` | Remove user |
|
||||
| `POST` | `/users/{userID}/suspend` | Suspend user |
|
||||
| `POST` | `/users/{userID}/unsuspend` | Unsuspend user |
|
||||
| `POST` | `/users/{userID}/password` | Set's or changes user's password |
|
||||
| `GET` | `/users/{userID}/membership` | Add member to a role |
|
||||
| `POST` | `/users/{userID}/membership/{roleID}` | Add role to a user |
|
||||
| `DELETE` | `/users/{userID}/membership/{roleID}` | Remove role from a user |
|
||||
|
||||
## Search users (Directory)
|
||||
|
||||
@@ -793,7 +796,7 @@ An organisation may have many roles. Roles may have many channels available. Acc
|
||||
| handle | string | POST | Handle | N/A | NO |
|
||||
| kind | types.UserKind | POST | Kind (normal, bot) | N/A | NO |
|
||||
|
||||
## Read user details and memberships
|
||||
## Read user details
|
||||
|
||||
#### Method
|
||||
|
||||
@@ -864,4 +867,48 @@ An organisation may have many roles. Roles may have many channels available. Acc
|
||||
| userID | uint64 | PATH | User ID | N/A | YES |
|
||||
| password | string | POST | New password | N/A | YES |
|
||||
|
||||
## Add member to a role
|
||||
|
||||
#### Method
|
||||
|
||||
| URI | Protocol | Method | Authentication |
|
||||
| --- | -------- | ------ | -------------- |
|
||||
| `/users/{userID}/membership` | HTTP/S | GET | Client ID, Session ID |
|
||||
|
||||
#### Request parameters
|
||||
|
||||
| Parameter | Type | Method | Description | Default | Required? |
|
||||
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||
| userID | uint64 | PATH | User ID | N/A | YES |
|
||||
|
||||
## Add role to a user
|
||||
|
||||
#### Method
|
||||
|
||||
| URI | Protocol | Method | Authentication |
|
||||
| --- | -------- | ------ | -------------- |
|
||||
| `/users/{userID}/membership/{roleID}` | HTTP/S | POST | Client ID, Session ID |
|
||||
|
||||
#### Request parameters
|
||||
|
||||
| Parameter | Type | Method | Description | Default | Required? |
|
||||
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||
| roleID | uint64 | PATH | Role ID | N/A | YES |
|
||||
| userID | uint64 | PATH | User ID | N/A | YES |
|
||||
|
||||
## Remove role from a user
|
||||
|
||||
#### Method
|
||||
|
||||
| URI | Protocol | Method | Authentication |
|
||||
| --- | -------- | ------ | -------------- |
|
||||
| `/users/{userID}/membership/{roleID}` | HTTP/S | DELETE | Client ID, Session ID |
|
||||
|
||||
#### Request parameters
|
||||
|
||||
| Parameter | Type | Method | Description | Default | Required? |
|
||||
| --------- | ---- | ------ | ----------- | ------- | --------- |
|
||||
| roleID | uint64 | PATH | Role ID | N/A | YES |
|
||||
| userID | uint64 | PATH | User ID | N/A | YES |
|
||||
|
||||
---
|
||||
@@ -27,6 +27,7 @@ type (
|
||||
MergeByID(id, targetRoleID uint64) error
|
||||
MoveByID(id, targetOrganisationID uint64) error
|
||||
|
||||
MembershipsFindByUserID(userID uint64) ([]*types.RoleMember, error)
|
||||
MemberFindByRoleID(roleID uint64) ([]*types.RoleMember, error)
|
||||
MemberAddByID(roleID, userID uint64) error
|
||||
MemberRemoveByID(roleID, userID uint64) error
|
||||
@@ -127,6 +128,12 @@ func (r *role) MoveByID(id, targetOrganisationID uint64) error {
|
||||
return ErrNotImplemented
|
||||
}
|
||||
|
||||
func (r *role) MembershipsFindByUserID(roleID uint64) (mm []*types.RoleMember, err error) {
|
||||
rval := make([]*types.RoleMember, 0)
|
||||
sql := "SELECT * FROM " + r.members + " WHERE rel_user = ?"
|
||||
return rval, r.db().Select(&rval, sql, roleID)
|
||||
}
|
||||
|
||||
func (r *role) MemberFindByRoleID(roleID uint64) (mm []*types.RoleMember, err error) {
|
||||
rval := make([]*types.RoleMember, 0)
|
||||
sql := "SELECT * FROM " + r.members + " WHERE rel_role = ?"
|
||||
|
||||
@@ -45,6 +45,7 @@ type (
|
||||
Unarchive(ID uint64) error
|
||||
Delete(ID uint64) error
|
||||
|
||||
Membership(userID uint64) ([]*types.RoleMember, error)
|
||||
MemberList(roleID uint64) ([]*types.RoleMember, error)
|
||||
MemberAdd(roleID, userID uint64) error
|
||||
MemberRemove(roleID, userID uint64) error
|
||||
@@ -225,6 +226,10 @@ func (svc role) Move(roleID, targetOrganisationID uint64) error {
|
||||
return svc.role.MoveByID(roleID, targetOrganisationID)
|
||||
}
|
||||
|
||||
func (svc role) Membership(userID uint64) ([]*types.RoleMember, error) {
|
||||
return svc.role.MembershipsFindByUserID(userID)
|
||||
}
|
||||
|
||||
func (svc role) MemberList(roleID uint64) ([]*types.RoleMember, error) {
|
||||
_, err := svc.findByID(roleID)
|
||||
if err != nil {
|
||||
|
||||
@@ -37,18 +37,24 @@ type UserAPI interface {
|
||||
Suspend(context.Context, *request.UserSuspend) (interface{}, error)
|
||||
Unsuspend(context.Context, *request.UserUnsuspend) (interface{}, error)
|
||||
SetPassword(context.Context, *request.UserSetPassword) (interface{}, error)
|
||||
MembershipList(context.Context, *request.UserMembershipList) (interface{}, error)
|
||||
MembershipAdd(context.Context, *request.UserMembershipAdd) (interface{}, error)
|
||||
MembershipRemove(context.Context, *request.UserMembershipRemove) (interface{}, error)
|
||||
}
|
||||
|
||||
// HTTP API interface
|
||||
type User struct {
|
||||
List func(http.ResponseWriter, *http.Request)
|
||||
Create func(http.ResponseWriter, *http.Request)
|
||||
Update func(http.ResponseWriter, *http.Request)
|
||||
Read func(http.ResponseWriter, *http.Request)
|
||||
Delete func(http.ResponseWriter, *http.Request)
|
||||
Suspend func(http.ResponseWriter, *http.Request)
|
||||
Unsuspend func(http.ResponseWriter, *http.Request)
|
||||
SetPassword func(http.ResponseWriter, *http.Request)
|
||||
List func(http.ResponseWriter, *http.Request)
|
||||
Create func(http.ResponseWriter, *http.Request)
|
||||
Update func(http.ResponseWriter, *http.Request)
|
||||
Read func(http.ResponseWriter, *http.Request)
|
||||
Delete func(http.ResponseWriter, *http.Request)
|
||||
Suspend func(http.ResponseWriter, *http.Request)
|
||||
Unsuspend func(http.ResponseWriter, *http.Request)
|
||||
SetPassword func(http.ResponseWriter, *http.Request)
|
||||
MembershipList func(http.ResponseWriter, *http.Request)
|
||||
MembershipAdd func(http.ResponseWriter, *http.Request)
|
||||
MembershipRemove func(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
|
||||
func NewUser(h UserAPI) *User {
|
||||
@@ -213,6 +219,66 @@ func NewUser(h UserAPI) *User {
|
||||
resputil.JSON(w, value)
|
||||
}
|
||||
},
|
||||
MembershipList: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewUserMembershipList()
|
||||
if err := params.Fill(r); err != nil {
|
||||
logger.LogParamError("User.MembershipList", r, err)
|
||||
resputil.JSON(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.MembershipList(r.Context(), params)
|
||||
if err != nil {
|
||||
logger.LogControllerError("User.MembershipList", r, err, params.Auditable())
|
||||
resputil.JSON(w, err)
|
||||
return
|
||||
}
|
||||
logger.LogControllerCall("User.MembershipList", r, params.Auditable())
|
||||
if !serveHTTP(value, w, r) {
|
||||
resputil.JSON(w, value)
|
||||
}
|
||||
},
|
||||
MembershipAdd: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewUserMembershipAdd()
|
||||
if err := params.Fill(r); err != nil {
|
||||
logger.LogParamError("User.MembershipAdd", r, err)
|
||||
resputil.JSON(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.MembershipAdd(r.Context(), params)
|
||||
if err != nil {
|
||||
logger.LogControllerError("User.MembershipAdd", r, err, params.Auditable())
|
||||
resputil.JSON(w, err)
|
||||
return
|
||||
}
|
||||
logger.LogControllerCall("User.MembershipAdd", r, params.Auditable())
|
||||
if !serveHTTP(value, w, r) {
|
||||
resputil.JSON(w, value)
|
||||
}
|
||||
},
|
||||
MembershipRemove: func(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
params := request.NewUserMembershipRemove()
|
||||
if err := params.Fill(r); err != nil {
|
||||
logger.LogParamError("User.MembershipRemove", r, err)
|
||||
resputil.JSON(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
value, err := h.MembershipRemove(r.Context(), params)
|
||||
if err != nil {
|
||||
logger.LogControllerError("User.MembershipRemove", r, err, params.Auditable())
|
||||
resputil.JSON(w, err)
|
||||
return
|
||||
}
|
||||
logger.LogControllerCall("User.MembershipRemove", r, params.Auditable())
|
||||
if !serveHTTP(value, w, r) {
|
||||
resputil.JSON(w, value)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,5 +293,8 @@ func (h User) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http.H
|
||||
r.Post("/users/{userID}/suspend", h.Suspend)
|
||||
r.Post("/users/{userID}/unsuspend", h.Unsuspend)
|
||||
r.Post("/users/{userID}/password", h.SetPassword)
|
||||
r.Get("/users/{userID}/membership", h.MembershipList)
|
||||
r.Post("/users/{userID}/membership/{roleID}", h.MembershipAdd)
|
||||
r.Delete("/users/{userID}/membership/{roleID}", h.MembershipRemove)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -523,3 +523,162 @@ func (r *UserSetPassword) Fill(req *http.Request) (err error) {
|
||||
}
|
||||
|
||||
var _ RequestFiller = NewUserSetPassword()
|
||||
|
||||
// User membershipList request parameters
|
||||
type UserMembershipList struct {
|
||||
UserID uint64 `json:",string"`
|
||||
}
|
||||
|
||||
func NewUserMembershipList() *UserMembershipList {
|
||||
return &UserMembershipList{}
|
||||
}
|
||||
|
||||
func (r UserMembershipList) Auditable() map[string]interface{} {
|
||||
var out = map[string]interface{}{}
|
||||
|
||||
out["userID"] = r.UserID
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (r *UserMembershipList) Fill(req *http.Request) (err error) {
|
||||
if strings.ToLower(req.Header.Get("content-type")) == "application/json" {
|
||||
err = json.NewDecoder(req.Body).Decode(r)
|
||||
|
||||
switch {
|
||||
case err == io.EOF:
|
||||
err = nil
|
||||
case err != nil:
|
||||
return errors.Wrap(err, "error parsing http request body")
|
||||
}
|
||||
}
|
||||
|
||||
if err = req.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
get := map[string]string{}
|
||||
post := map[string]string{}
|
||||
urlQuery := req.URL.Query()
|
||||
for name, param := range urlQuery {
|
||||
get[name] = string(param[0])
|
||||
}
|
||||
postVars := req.Form
|
||||
for name, param := range postVars {
|
||||
post[name] = string(param[0])
|
||||
}
|
||||
|
||||
r.UserID = parseUInt64(chi.URLParam(req, "userID"))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
var _ RequestFiller = NewUserMembershipList()
|
||||
|
||||
// User membershipAdd request parameters
|
||||
type UserMembershipAdd struct {
|
||||
RoleID uint64 `json:",string"`
|
||||
UserID uint64 `json:",string"`
|
||||
}
|
||||
|
||||
func NewUserMembershipAdd() *UserMembershipAdd {
|
||||
return &UserMembershipAdd{}
|
||||
}
|
||||
|
||||
func (r UserMembershipAdd) Auditable() map[string]interface{} {
|
||||
var out = map[string]interface{}{}
|
||||
|
||||
out["roleID"] = r.RoleID
|
||||
out["userID"] = r.UserID
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (r *UserMembershipAdd) Fill(req *http.Request) (err error) {
|
||||
if strings.ToLower(req.Header.Get("content-type")) == "application/json" {
|
||||
err = json.NewDecoder(req.Body).Decode(r)
|
||||
|
||||
switch {
|
||||
case err == io.EOF:
|
||||
err = nil
|
||||
case err != nil:
|
||||
return errors.Wrap(err, "error parsing http request body")
|
||||
}
|
||||
}
|
||||
|
||||
if err = req.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
get := map[string]string{}
|
||||
post := map[string]string{}
|
||||
urlQuery := req.URL.Query()
|
||||
for name, param := range urlQuery {
|
||||
get[name] = string(param[0])
|
||||
}
|
||||
postVars := req.Form
|
||||
for name, param := range postVars {
|
||||
post[name] = string(param[0])
|
||||
}
|
||||
|
||||
r.RoleID = parseUInt64(chi.URLParam(req, "roleID"))
|
||||
r.UserID = parseUInt64(chi.URLParam(req, "userID"))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
var _ RequestFiller = NewUserMembershipAdd()
|
||||
|
||||
// User membershipRemove request parameters
|
||||
type UserMembershipRemove struct {
|
||||
RoleID uint64 `json:",string"`
|
||||
UserID uint64 `json:",string"`
|
||||
}
|
||||
|
||||
func NewUserMembershipRemove() *UserMembershipRemove {
|
||||
return &UserMembershipRemove{}
|
||||
}
|
||||
|
||||
func (r UserMembershipRemove) Auditable() map[string]interface{} {
|
||||
var out = map[string]interface{}{}
|
||||
|
||||
out["roleID"] = r.RoleID
|
||||
out["userID"] = r.UserID
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (r *UserMembershipRemove) Fill(req *http.Request) (err error) {
|
||||
if strings.ToLower(req.Header.Get("content-type")) == "application/json" {
|
||||
err = json.NewDecoder(req.Body).Decode(r)
|
||||
|
||||
switch {
|
||||
case err == io.EOF:
|
||||
err = nil
|
||||
case err != nil:
|
||||
return errors.Wrap(err, "error parsing http request body")
|
||||
}
|
||||
}
|
||||
|
||||
if err = req.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
get := map[string]string{}
|
||||
post := map[string]string{}
|
||||
urlQuery := req.URL.Query()
|
||||
for name, param := range urlQuery {
|
||||
get[name] = string(param[0])
|
||||
}
|
||||
postVars := req.Form
|
||||
for name, param := range postVars {
|
||||
post[name] = string(param[0])
|
||||
}
|
||||
|
||||
r.RoleID = parseUInt64(chi.URLParam(req, "roleID"))
|
||||
r.UserID = parseUInt64(chi.URLParam(req, "userID"))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
var _ RequestFiller = NewUserMembershipRemove()
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/titpetric/factory/resputil"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/internal/payload"
|
||||
"github.com/cortezaproject/corteza-server/system/internal/service"
|
||||
"github.com/cortezaproject/corteza-server/system/rest/request"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
@@ -16,6 +17,7 @@ var _ = errors.Wrap
|
||||
type (
|
||||
User struct {
|
||||
user service.UserService
|
||||
role service.RoleService
|
||||
}
|
||||
|
||||
userSetPayload struct {
|
||||
@@ -27,6 +29,7 @@ type (
|
||||
func (User) New() *User {
|
||||
ctrl := &User{}
|
||||
ctrl.user = service.DefaultUser
|
||||
ctrl.role = service.DefaultRole
|
||||
return ctrl
|
||||
}
|
||||
|
||||
@@ -89,6 +92,26 @@ func (ctrl User) SetPassword(ctx context.Context, r *request.UserSetPassword) (i
|
||||
return resputil.OK(), ctrl.user.With(ctx).SetPassword(r.UserID, r.Password)
|
||||
}
|
||||
|
||||
func (ctrl User) MembershipList(ctx context.Context, r *request.UserMembershipList) (interface{}, error) {
|
||||
if mm, err := ctrl.role.With(ctx).Membership(r.UserID); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
rval := make([]string, len(mm))
|
||||
for i := range mm {
|
||||
rval[i] = payload.Uint64toa(mm[i].RoleID)
|
||||
}
|
||||
return rval, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (ctrl User) MembershipAdd(ctx context.Context, r *request.UserMembershipAdd) (interface{}, error) {
|
||||
return resputil.OK(), ctrl.role.With(ctx).MemberAdd(r.RoleID, r.UserID)
|
||||
}
|
||||
|
||||
func (ctrl User) MembershipRemove(ctx context.Context, r *request.UserMembershipRemove) (interface{}, error) {
|
||||
return resputil.OK(), ctrl.role.With(ctx).MemberRemove(r.RoleID, r.UserID)
|
||||
}
|
||||
|
||||
func (ctrl User) makeFilterPayload(ctx context.Context, uu types.UserSet, f types.UserFilter, err error) (*userSetPayload, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
Reference in New Issue
Block a user