From e4d78904dcb2cf49da3b8b3649f1698615b9da9c Mon Sep 17 00:00:00 2001 From: sauls8t Date: Wed, 28 Feb 2018 15:39:46 +0000 Subject: [PATCH] join and leave group persistence --- domain/group/endpoint.go | 104 ++++++++++++++++++ domain/group/mysql/store.go | 21 ++++ domain/storer.go | 2 + gui/app/components/customize/user-groups.js | 64 ++++++----- .../components/customize/user-groups.hbs | 2 +- model/audit/audit.go | 2 + 6 files changed, 161 insertions(+), 34 deletions(-) diff --git a/domain/group/endpoint.go b/domain/group/endpoint.go index 38c33ff2..00e45cac 100644 --- a/domain/group/endpoint.go +++ b/domain/group/endpoint.go @@ -243,3 +243,107 @@ func (h *Handler) GetGroupMembers(w http.ResponseWriter, r *http.Request) { response.WriteJSON(w, m) } + +// JoinGroup adds user to group. +func (h *Handler) JoinGroup(w http.ResponseWriter, r *http.Request) { + method := "group.JoinGroup" + ctx := domain.GetRequestContext(r) + + // Should be no reason for non-admin to see members + if !ctx.Administrator { + response.WriteForbiddenError(w) + return + } + + groupID := request.Param(r, "groupID") + if len(groupID) == 0 { + response.WriteMissingDataError(w, method, "groupID") + return + } + + userID := request.Param(r, "userID") + if len(userID) == 0 { + response.WriteMissingDataError(w, method, "userID") + return + } + + var err error + + ctx.Transaction, err = h.Runtime.Db.Beginx() + if err != nil { + response.WriteServerError(w, method, err) + h.Runtime.Log.Error(method, err) + return + } + + // safety first + err = h.Store.Group.LeaveGroup(ctx, groupID, userID) + if err != nil { + ctx.Transaction.Rollback() + response.WriteServerError(w, method, err) + h.Runtime.Log.Error(method, err) + return + } + + // now we can join group + err = h.Store.Group.JoinGroup(ctx, groupID, userID) + if err != nil { + ctx.Transaction.Rollback() + response.WriteServerError(w, method, err) + h.Runtime.Log.Error(method, err) + return + } + + ctx.Transaction.Commit() + + h.Store.Audit.Record(ctx, audit.EventTypeGroupJoin) + + response.WriteEmpty(w) +} + +// LeaveGroup removes user to group. +func (h *Handler) LeaveGroup(w http.ResponseWriter, r *http.Request) { + method := "group.LeaveGroup" + ctx := domain.GetRequestContext(r) + + // Should be no reason for non-admin to see members + if !ctx.Administrator { + response.WriteForbiddenError(w) + return + } + + groupID := request.Param(r, "groupID") + if len(groupID) == 0 { + response.WriteMissingDataError(w, method, "groupID") + return + } + + userID := request.Param(r, "userID") + if len(userID) == 0 { + response.WriteMissingDataError(w, method, "userID") + return + } + + var err error + + ctx.Transaction, err = h.Runtime.Db.Beginx() + if err != nil { + response.WriteServerError(w, method, err) + h.Runtime.Log.Error(method, err) + return + } + + err = h.Store.Group.LeaveGroup(ctx, groupID, userID) + if err != nil { + ctx.Transaction.Rollback() + response.WriteServerError(w, method, err) + h.Runtime.Log.Error(method, err) + return + } + + ctx.Transaction.Commit() + + h.Store.Audit.Record(ctx, audit.EventTypeGroupLeave) + + response.WriteEmpty(w) +} diff --git a/domain/group/mysql/store.go b/domain/group/mysql/store.go index d41cbfe0..3a7015c7 100644 --- a/domain/group/mysql/store.go +++ b/domain/group/mysql/store.go @@ -120,3 +120,24 @@ func (s Scope) GetGroupMembers(ctx domain.RequestContext, groupID string) (membe return } + +// JoinGroup adds user to group. +func (s Scope) JoinGroup(ctx domain.RequestContext, groupID, userID string) (err error) { + _, err = ctx.Transaction.Exec("INSERT INTO rolemember (orgid, roleid, userid) VALUES (?, ?, ?)", ctx.OrgID, groupID, userID) + if err != nil { + err = errors.Wrap(err, "insert group member") + } + + return +} + +// LeaveGroup removes user from group. +func (s Scope) LeaveGroup(ctx domain.RequestContext, groupID, userID string) (err error) { + b := mysql.BaseQuery{} + _, err = b.DeleteWhere(ctx.Transaction, fmt.Sprintf("DELETE FROM rolemember WHERE orgid=\"%s\" AND roleid=\"%s\" AND userid=\"%s\"", ctx.OrgID, groupID, userID)) + if err != nil { + err = errors.Wrap(err, "clear group member") + } + + return +} diff --git a/domain/storer.go b/domain/storer.go index fb08f65a..0562e8a7 100644 --- a/domain/storer.go +++ b/domain/storer.go @@ -277,4 +277,6 @@ type GroupStorer interface { Update(ctx RequestContext, g group.Group) (err error) Delete(ctx RequestContext, refID string) (rows int64, err error) GetGroupMembers(ctx RequestContext, groupID string) (m []group.Member, err error) + JoinGroup(ctx RequestContext, groupID, userID string) (err error) + LeaveGroup(ctx RequestContext, groupID, userID string) (err error) } diff --git a/gui/app/components/customize/user-groups.js b/gui/app/components/customize/user-groups.js index ae61b5e3..8c47b984 100644 --- a/gui/app/components/customize/user-groups.js +++ b/gui/app/components/customize/user-groups.js @@ -28,11 +28,11 @@ export default Component.extend(AuthProvider, ModalMixin, { didReceiveAttrs() { this._super(...arguments); - this.load(); + this.loadGroups(); this.setDefaults(); }, - load() { + loadGroups() { this.get('groupSvc').getAll().then((groups) => { this.set('groups', groups); }); @@ -42,34 +42,26 @@ export default Component.extend(AuthProvider, ModalMixin, { this.set('newGroup', { name: '', purpose: '' }); }, - loadUsers(searchText) { - this.get('userSvc').matchUsers(searchText).then((users) => { - let members = this.get('members'); - - if (members.length > 0) { + loadGroupInfo() { + let groupId = this.get('membersGroup.id'); + let searchText = this.get('searchText'); + + this.get('groupSvc').getGroupMembers(groupId).then((members) => { + this.set('members', members); + + this.get('userSvc').matchUsers(searchText).then((users) => { users.forEach((user) => { let m = members.findBy('userId', user.get('id')); user.set('isMember', is.not.undefined(m)); }) - } - this.set('users', users); - }); - }, - - loadMembers(groupId) { - this.get('groupSvc').getGroupMembers(groupId).then((members) => { - this.set('members', members); - - // if we have no members, then prefetch users (server should limit to top 100 users) - if (members.length === 0) { - this.loadUsers(''); - this.set('showMembers', false); - this.set('showUsers', true); - } else { - this.set('showMembers', true); - this.set('showUsers', false); - } + if (this.get('showMembers') && members.length === 0) { + this.set('showMembers', false); + this.set('showUsers', true); + } + + this.set('users', users); + }); }); }, @@ -148,20 +140,20 @@ export default Component.extend(AuthProvider, ModalMixin, { this.modalOpen("#group-members-modal", {"show": true}, '#group-members-search'); this.set('members', null); this.set('users', null); - this.loadMembers(groupId); + this.set('showMembers', true); + this.set('showUsers', false); + this.loadGroupInfo(); }, onSearch() { debounce(this, function() { let searchText = this.get('searchText'); - let groupId = this.get('membersGroup.id'); + this.loadGroupInfo(); if (is.not.empty(searchText)) { - this.loadUsers(searchText); this.set('showMembers', false); this.set('showUsers', true); } else { - this.loadMembers(groupId); this.set('showMembers', true); this.set('showUsers', false); } @@ -169,14 +161,20 @@ export default Component.extend(AuthProvider, ModalMixin, { }, onLeaveGroup(userId) { - this.get('groupSvc').leave(this.get('membersGroup.id'), userId).then(() => { - this.load(); + let groupId = this.get('membersGroup.id'); + + this.get('groupSvc').leave(groupId, userId).then(() => { + this.loadGroupInfo(); + this.loadGroups(); }); }, onJoinGroup(userId) { - this.get('groupSvc').join(this.get('membersGroup.id'), userId).then(() => { - this.load(); + let groupId = this.get('membersGroup.id'); + + this.get('groupSvc').join(groupId, userId).then(() => { + this.loadGroupInfo(); + this.loadGroups(); }); } } diff --git a/gui/app/templates/components/customize/user-groups.hbs b/gui/app/templates/components/customize/user-groups.hbs index c1a7da2a..5d047a69 100644 --- a/gui/app/templates/components/customize/user-groups.hbs +++ b/gui/app/templates/components/customize/user-groups.hbs @@ -110,7 +110,7 @@