1// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2// See LICENSE.txt for license information.
3
4package api4
5
6import (
7	"encoding/json"
8	"fmt"
9	"io/ioutil"
10	"net/http"
11	"strconv"
12	"strings"
13
14	"github.com/mattermost/mattermost-server/v6/audit"
15	"github.com/mattermost/mattermost-server/v6/model"
16)
17
18func (api *API) InitGroup() {
19	// GET /api/v4/groups
20	api.BaseRoutes.Groups.Handle("", api.APISessionRequired(getGroups)).Methods("GET")
21
22	// GET /api/v4/groups/:group_id
23	api.BaseRoutes.Groups.Handle("/{group_id:[A-Za-z0-9]+}",
24		api.APISessionRequired(getGroup)).Methods("GET")
25
26	// PUT /api/v4/groups/:group_id/patch
27	api.BaseRoutes.Groups.Handle("/{group_id:[A-Za-z0-9]+}/patch",
28		api.APISessionRequired(patchGroup)).Methods("PUT")
29
30	// POST /api/v4/groups/:group_id/teams/:team_id/link
31	// POST /api/v4/groups/:group_id/channels/:channel_id/link
32	api.BaseRoutes.Groups.Handle("/{group_id:[A-Za-z0-9]+}/{syncable_type:teams|channels}/{syncable_id:[A-Za-z0-9]+}/link",
33		api.APISessionRequired(linkGroupSyncable)).Methods("POST")
34
35	// DELETE /api/v4/groups/:group_id/teams/:team_id/link
36	// DELETE /api/v4/groups/:group_id/channels/:channel_id/link
37	api.BaseRoutes.Groups.Handle("/{group_id:[A-Za-z0-9]+}/{syncable_type:teams|channels}/{syncable_id:[A-Za-z0-9]+}/link",
38		api.APISessionRequired(unlinkGroupSyncable)).Methods("DELETE")
39
40	// GET /api/v4/groups/:group_id/teams/:team_id
41	// GET /api/v4/groups/:group_id/channels/:channel_id
42	api.BaseRoutes.Groups.Handle("/{group_id:[A-Za-z0-9]+}/{syncable_type:teams|channels}/{syncable_id:[A-Za-z0-9]+}",
43		api.APISessionRequired(getGroupSyncable)).Methods("GET")
44
45	// GET /api/v4/groups/:group_id/teams
46	// GET /api/v4/groups/:group_id/channels
47	api.BaseRoutes.Groups.Handle("/{group_id:[A-Za-z0-9]+}/{syncable_type:teams|channels}",
48		api.APISessionRequired(getGroupSyncables)).Methods("GET")
49
50	// PUT /api/v4/groups/:group_id/teams/:team_id/patch
51	// PUT /api/v4/groups/:group_id/channels/:channel_id/patch
52	api.BaseRoutes.Groups.Handle("/{group_id:[A-Za-z0-9]+}/{syncable_type:teams|channels}/{syncable_id:[A-Za-z0-9]+}/patch",
53		api.APISessionRequired(patchGroupSyncable)).Methods("PUT")
54
55	// GET /api/v4/groups/:group_id/stats
56	api.BaseRoutes.Groups.Handle("/{group_id:[A-Za-z0-9]+}/stats",
57		api.APISessionRequired(getGroupStats)).Methods("GET")
58
59	// GET /api/v4/groups/:group_id/members?page=0&per_page=100
60	api.BaseRoutes.Groups.Handle("/{group_id:[A-Za-z0-9]+}/members",
61		api.APISessionRequired(getGroupMembers)).Methods("GET")
62
63	// GET /api/v4/users/:user_id/groups?page=0&per_page=100
64	api.BaseRoutes.Users.Handle("/{user_id:[A-Za-z0-9]+}/groups",
65		api.APISessionRequired(getGroupsByUserId)).Methods("GET")
66
67	// GET /api/v4/channels/:channel_id/groups?page=0&per_page=100
68	api.BaseRoutes.Channels.Handle("/{channel_id:[A-Za-z0-9]+}/groups",
69		api.APISessionRequired(getGroupsByChannel)).Methods("GET")
70
71	// GET /api/v4/teams/:team_id/groups?page=0&per_page=100
72	api.BaseRoutes.Teams.Handle("/{team_id:[A-Za-z0-9]+}/groups",
73		api.APISessionRequired(getGroupsByTeam)).Methods("GET")
74
75	// GET /api/v4/teams/:team_id/groups_by_channels?page=0&per_page=100
76	api.BaseRoutes.Teams.Handle("/{team_id:[A-Za-z0-9]+}/groups_by_channels",
77		api.APISessionRequired(getGroupsAssociatedToChannelsByTeam)).Methods("GET")
78}
79
80func getGroup(c *Context, w http.ResponseWriter, r *http.Request) {
81	c.RequireGroupId()
82	if c.Err != nil {
83		return
84	}
85
86	if c.App.Srv().License() == nil || !*c.App.Srv().License().Features.LDAPGroups {
87		c.Err = model.NewAppError("Api4.getGroup", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
88		return
89	}
90
91	if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionSysconsoleReadUserManagementGroups) {
92		c.SetPermissionError(model.PermissionSysconsoleReadUserManagementGroups)
93		return
94	}
95
96	group, err := c.App.GetGroup(c.Params.GroupId)
97	if err != nil {
98		c.Err = err
99		return
100	}
101
102	b, marshalErr := json.Marshal(group)
103	if marshalErr != nil {
104		c.Err = model.NewAppError("Api4.getGroup", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
105		return
106	}
107
108	w.Write(b)
109}
110
111func patchGroup(c *Context, w http.ResponseWriter, r *http.Request) {
112	c.RequireGroupId()
113	if c.Err != nil {
114		return
115	}
116
117	var groupPatch model.GroupPatch
118	if jsonErr := json.NewDecoder(r.Body).Decode(&groupPatch); jsonErr != nil {
119		c.SetInvalidParam("group")
120		return
121	}
122
123	auditRec := c.MakeAuditRecord("patchGroup", audit.Fail)
124	defer c.LogAuditRec(auditRec)
125
126	if c.App.Srv().License() == nil || !*c.App.Srv().License().Features.LDAPGroups {
127		c.Err = model.NewAppError("Api4.patchGroup", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
128		return
129	}
130
131	if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionSysconsoleWriteUserManagementGroups) {
132		c.SetPermissionError(model.PermissionSysconsoleWriteUserManagementGroups)
133		return
134	}
135
136	group, err := c.App.GetGroup(c.Params.GroupId)
137	if err != nil {
138		c.Err = err
139		return
140	}
141	auditRec.AddMeta("group", group)
142
143	if groupPatch.AllowReference != nil && *groupPatch.AllowReference {
144		if groupPatch.Name == nil {
145			tmp := strings.ReplaceAll(strings.ToLower(group.DisplayName), " ", "-")
146			groupPatch.Name = &tmp
147		} else {
148			if *groupPatch.Name == model.UserNotifyAll || *groupPatch.Name == model.ChannelMentionsNotifyProp || *groupPatch.Name == model.UserNotifyHere {
149				c.Err = model.NewAppError("Api4.patchGroup", "api.ldap_groups.existing_reserved_name_error", nil, "", http.StatusNotImplemented)
150				return
151			}
152			//check if a user already has this group name
153			user, _ := c.App.GetUserByUsername(*groupPatch.Name)
154			if user != nil {
155				c.Err = model.NewAppError("Api4.patchGroup", "api.ldap_groups.existing_user_name_error", nil, "", http.StatusNotImplemented)
156				return
157			}
158			//check if a mentionable group already has this name
159			searchOpts := model.GroupSearchOpts{
160				FilterAllowReference: true,
161			}
162			existingGroup, _ := c.App.GetGroupByName(*groupPatch.Name, searchOpts)
163			if existingGroup != nil {
164				c.Err = model.NewAppError("Api4.patchGroup", "api.ldap_groups.existing_group_name_error", nil, "", http.StatusNotImplemented)
165				return
166			}
167		}
168	}
169
170	group.Patch(&groupPatch)
171
172	group, err = c.App.UpdateGroup(group)
173	if err != nil {
174		c.Err = err
175		return
176	}
177	auditRec.AddMeta("patch", group)
178
179	b, marshalErr := json.Marshal(group)
180	if marshalErr != nil {
181		c.Err = model.NewAppError("Api4.patchGroup", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
182		return
183	}
184
185	auditRec.Success()
186	w.Write(b)
187}
188
189func linkGroupSyncable(c *Context, w http.ResponseWriter, r *http.Request) {
190	c.RequireGroupId()
191	if c.Err != nil {
192		return
193	}
194
195	c.RequireSyncableId()
196	if c.Err != nil {
197		return
198	}
199	syncableID := c.Params.SyncableId
200
201	c.RequireSyncableType()
202	if c.Err != nil {
203		return
204	}
205	syncableType := c.Params.SyncableType
206
207	body, err := ioutil.ReadAll(r.Body)
208	if err != nil {
209		c.Err = model.NewAppError("Api4.createGroupSyncable", "api.io_error", nil, err.Error(), http.StatusBadRequest)
210		return
211	}
212
213	auditRec := c.MakeAuditRecord("linkGroupSyncable", audit.Fail)
214	defer c.LogAuditRec(auditRec)
215	auditRec.AddMeta("group_id", c.Params.GroupId)
216	auditRec.AddMeta("syncable_id", syncableID)
217	auditRec.AddMeta("syncable_type", syncableType)
218
219	var patch *model.GroupSyncablePatch
220	err = json.Unmarshal(body, &patch)
221	if err != nil || patch == nil {
222		c.SetInvalidParam(fmt.Sprintf("Group%s", syncableType.String()))
223		return
224	}
225
226	if c.App.Srv().License() == nil || !*c.App.Srv().License().Features.LDAPGroups {
227		c.Err = model.NewAppError("Api4.createGroupSyncable", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
228		return
229	}
230
231	appErr := verifyLinkUnlinkPermission(c, syncableType, syncableID)
232	if appErr != nil {
233		c.Err = appErr
234		return
235	}
236
237	groupSyncable := &model.GroupSyncable{
238		GroupId:    c.Params.GroupId,
239		SyncableId: syncableID,
240		Type:       syncableType,
241	}
242	groupSyncable.Patch(patch)
243	groupSyncable, appErr = c.App.UpsertGroupSyncable(groupSyncable)
244	if appErr != nil {
245		c.Err = appErr
246		return
247	}
248
249	c.App.Srv().Go(func() {
250		c.App.SyncRolesAndMembership(c.AppContext, syncableID, syncableType, false)
251	})
252
253	w.WriteHeader(http.StatusCreated)
254
255	b, marshalErr := json.Marshal(groupSyncable)
256	if marshalErr != nil {
257		c.Err = model.NewAppError("Api4.createGroupSyncable", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
258		return
259	}
260	auditRec.Success()
261	w.Write(b)
262}
263
264func getGroupSyncable(c *Context, w http.ResponseWriter, r *http.Request) {
265	c.RequireGroupId()
266	if c.Err != nil {
267		return
268	}
269
270	c.RequireSyncableId()
271	if c.Err != nil {
272		return
273	}
274	syncableID := c.Params.SyncableId
275
276	c.RequireSyncableType()
277	if c.Err != nil {
278		return
279	}
280	syncableType := c.Params.SyncableType
281
282	if c.App.Srv().License() == nil || !*c.App.Srv().License().Features.LDAPGroups {
283		c.Err = model.NewAppError("Api4.getGroupSyncable", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
284		return
285	}
286
287	if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem) {
288		c.SetPermissionError(model.PermissionManageSystem)
289		return
290	}
291
292	groupSyncable, err := c.App.GetGroupSyncable(c.Params.GroupId, syncableID, syncableType)
293	if err != nil {
294		c.Err = err
295		return
296	}
297
298	b, marshalErr := json.Marshal(groupSyncable)
299	if marshalErr != nil {
300		c.Err = model.NewAppError("Api4.getGroupSyncable", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
301		return
302	}
303
304	w.Write(b)
305}
306
307func getGroupSyncables(c *Context, w http.ResponseWriter, r *http.Request) {
308	c.RequireGroupId()
309	if c.Err != nil {
310		return
311	}
312
313	c.RequireSyncableType()
314	if c.Err != nil {
315		return
316	}
317	syncableType := c.Params.SyncableType
318
319	if c.App.Srv().License() == nil || !*c.App.Srv().License().Features.LDAPGroups {
320		c.Err = model.NewAppError("Api4.getGroupSyncables", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
321		return
322	}
323
324	if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionSysconsoleReadUserManagementGroups) {
325		c.SetPermissionError(model.PermissionSysconsoleReadUserManagementGroups)
326		return
327	}
328
329	groupSyncables, err := c.App.GetGroupSyncables(c.Params.GroupId, syncableType)
330	if err != nil {
331		c.Err = err
332		return
333	}
334
335	b, marshalErr := json.Marshal(groupSyncables)
336	if marshalErr != nil {
337		c.Err = model.NewAppError("Api4.getGroupSyncables", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
338		return
339	}
340
341	w.Write(b)
342}
343
344func patchGroupSyncable(c *Context, w http.ResponseWriter, r *http.Request) {
345	c.RequireGroupId()
346	if c.Err != nil {
347		return
348	}
349
350	c.RequireSyncableId()
351	if c.Err != nil {
352		return
353	}
354	syncableID := c.Params.SyncableId
355
356	c.RequireSyncableType()
357	if c.Err != nil {
358		return
359	}
360	syncableType := c.Params.SyncableType
361
362	body, err := ioutil.ReadAll(r.Body)
363	if err != nil {
364		c.Err = model.NewAppError("Api4.patchGroupSyncable", "api.io_error", nil, err.Error(), http.StatusBadRequest)
365		return
366	}
367
368	auditRec := c.MakeAuditRecord("patchGroupSyncable", audit.Fail)
369	defer c.LogAuditRec(auditRec)
370	auditRec.AddMeta("group_id", c.Params.GroupId)
371	auditRec.AddMeta("old_syncable_id", syncableID)
372	auditRec.AddMeta("old_syncable_type", syncableType)
373
374	var patch *model.GroupSyncablePatch
375	err = json.Unmarshal(body, &patch)
376	if err != nil || patch == nil {
377		c.SetInvalidParam(fmt.Sprintf("Group[%s]Patch", syncableType.String()))
378		return
379	}
380
381	if c.App.Srv().License() == nil || !*c.App.Srv().License().Features.LDAPGroups {
382		c.Err = model.NewAppError("Api4.patchGroupSyncable", "api.ldap_groups.license_error", nil, "",
383			http.StatusNotImplemented)
384		return
385	}
386
387	appErr := verifyLinkUnlinkPermission(c, syncableType, syncableID)
388	if appErr != nil {
389		c.Err = appErr
390		return
391	}
392
393	groupSyncable, appErr := c.App.GetGroupSyncable(c.Params.GroupId, syncableID, syncableType)
394	if appErr != nil {
395		c.Err = appErr
396		return
397	}
398
399	groupSyncable.Patch(patch)
400
401	groupSyncable, appErr = c.App.UpdateGroupSyncable(groupSyncable)
402	if appErr != nil {
403		c.Err = appErr
404		return
405	}
406
407	auditRec.AddMeta("new_syncable_id", groupSyncable.SyncableId)
408	auditRec.AddMeta("new_syncable_type", groupSyncable.Type)
409
410	c.App.Srv().Go(func() {
411		c.App.SyncRolesAndMembership(c.AppContext, syncableID, syncableType, false)
412	})
413
414	b, marshalErr := json.Marshal(groupSyncable)
415	if marshalErr != nil {
416		c.Err = model.NewAppError("Api4.patchGroupSyncable", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
417		return
418	}
419	auditRec.Success()
420	w.Write(b)
421}
422
423func unlinkGroupSyncable(c *Context, w http.ResponseWriter, r *http.Request) {
424	c.RequireGroupId()
425	if c.Err != nil {
426		return
427	}
428
429	c.RequireSyncableId()
430	if c.Err != nil {
431		return
432	}
433	syncableID := c.Params.SyncableId
434
435	c.RequireSyncableType()
436	if c.Err != nil {
437		return
438	}
439	syncableType := c.Params.SyncableType
440
441	auditRec := c.MakeAuditRecord("unlinkGroupSyncable", audit.Fail)
442	defer c.LogAuditRec(auditRec)
443	auditRec.AddMeta("group_id", c.Params.GroupId)
444	auditRec.AddMeta("syncable_id", syncableID)
445	auditRec.AddMeta("syncable_type", syncableType)
446
447	if c.App.Srv().License() == nil || !*c.App.Srv().License().Features.LDAPGroups {
448		c.Err = model.NewAppError("Api4.unlinkGroupSyncable", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
449		return
450	}
451
452	err := verifyLinkUnlinkPermission(c, syncableType, syncableID)
453	if err != nil {
454		c.Err = err
455		return
456	}
457
458	_, err = c.App.DeleteGroupSyncable(c.Params.GroupId, syncableID, syncableType)
459	if err != nil {
460		c.Err = err
461		return
462	}
463
464	c.App.Srv().Go(func() {
465		c.App.SyncRolesAndMembership(c.AppContext, syncableID, syncableType, false)
466	})
467
468	auditRec.Success()
469
470	ReturnStatusOK(w)
471}
472
473func verifyLinkUnlinkPermission(c *Context, syncableType model.GroupSyncableType, syncableID string) *model.AppError {
474	switch syncableType {
475	case model.GroupSyncableTypeTeam:
476		if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), syncableID, model.PermissionManageTeam) {
477			return c.App.MakePermissionError(c.AppContext.Session(), []*model.Permission{model.PermissionManageTeam})
478		}
479	case model.GroupSyncableTypeChannel:
480		channel, err := c.App.GetChannel(syncableID)
481		if err != nil {
482			return err
483		}
484
485		var permission *model.Permission
486		if channel.Type == model.ChannelTypePrivate {
487			permission = model.PermissionManagePrivateChannelMembers
488		} else {
489			permission = model.PermissionManagePublicChannelMembers
490		}
491
492		if !c.App.SessionHasPermissionToChannel(*c.AppContext.Session(), syncableID, permission) {
493			return c.App.MakePermissionError(c.AppContext.Session(), []*model.Permission{permission})
494		}
495	}
496
497	return nil
498}
499
500func getGroupMembers(c *Context, w http.ResponseWriter, r *http.Request) {
501	c.RequireGroupId()
502	if c.Err != nil {
503		return
504	}
505
506	if c.App.Srv().License() == nil || !*c.App.Srv().License().Features.LDAPGroups {
507		c.Err = model.NewAppError("Api4.getGroupMembers", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
508		return
509	}
510
511	if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionSysconsoleReadUserManagementGroups) {
512		c.SetPermissionError(model.PermissionSysconsoleReadUserManagementGroups)
513		return
514	}
515
516	members, count, err := c.App.GetGroupMemberUsersPage(c.Params.GroupId, c.Params.Page, c.Params.PerPage)
517	if err != nil {
518		c.Err = err
519		return
520	}
521
522	b, marshalErr := json.Marshal(struct {
523		Members []*model.User `json:"members"`
524		Count   int           `json:"total_member_count"`
525	}{
526		Members: members,
527		Count:   count,
528	})
529	if marshalErr != nil {
530		c.Err = model.NewAppError("Api4.getGroupMembers", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
531		return
532	}
533
534	w.Write(b)
535}
536
537func getGroupStats(c *Context, w http.ResponseWriter, r *http.Request) {
538	c.RequireGroupId()
539	if c.Err != nil {
540		return
541	}
542
543	if c.App.Srv().License() == nil || !*c.App.Srv().License().Features.LDAPGroups {
544		c.Err = model.NewAppError("Api4.getGroupStats", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
545		return
546	}
547
548	if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionSysconsoleReadUserManagementGroups) {
549		c.SetPermissionError(model.PermissionSysconsoleReadUserManagementGroups)
550		return
551	}
552
553	groupID := c.Params.GroupId
554	count, err := c.App.GetGroupMemberCount(groupID)
555	if err != nil {
556		c.Err = err
557		return
558	}
559
560	b, marshalErr := json.Marshal(model.GroupStats{
561		GroupID:          groupID,
562		TotalMemberCount: count,
563	})
564	if marshalErr != nil {
565		c.Err = model.NewAppError("Api4.getGroupStats", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
566		return
567	}
568
569	w.Write(b)
570}
571
572func getGroupsByUserId(c *Context, w http.ResponseWriter, r *http.Request) {
573	c.RequireUserId()
574	if c.Err != nil {
575		return
576	}
577
578	if c.AppContext.Session().UserId != c.Params.UserId && !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionManageSystem) {
579		c.SetPermissionError(model.PermissionManageSystem)
580		return
581	}
582
583	if c.App.Srv().License() == nil || !*c.App.Srv().License().Features.LDAPGroups {
584		c.Err = model.NewAppError("Api4.getGroupsByUserId", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
585		return
586	}
587
588	groups, err := c.App.GetGroupsByUserId(c.Params.UserId)
589	if err != nil {
590		c.Err = err
591		return
592	}
593
594	b, marshalErr := json.Marshal(groups)
595	if marshalErr != nil {
596		c.Err = model.NewAppError("Api4.getGroupsByUserId", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
597		return
598	}
599
600	w.Write(b)
601}
602
603func getGroupsByChannel(c *Context, w http.ResponseWriter, r *http.Request) {
604	c.RequireChannelId()
605	if c.Err != nil {
606		return
607	}
608
609	if c.App.Srv().License() == nil || !*c.App.Srv().License().Features.LDAPGroups {
610		c.Err = model.NewAppError("Api4.getGroupsByChannel", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
611		return
612	}
613
614	channel, err := c.App.GetChannel(c.Params.ChannelId)
615	if err != nil {
616		c.Err = err
617		return
618	}
619	var permission *model.Permission
620	if channel.Type == model.ChannelTypePrivate {
621		permission = model.PermissionReadPrivateChannelGroups
622	} else {
623		permission = model.PermissionReadPublicChannelGroups
624	}
625	if !c.App.SessionHasPermissionToChannel(*c.AppContext.Session(), c.Params.ChannelId, permission) {
626		c.SetPermissionError(permission)
627		return
628	}
629
630	opts := model.GroupSearchOpts{
631		Q:                    c.Params.Q,
632		IncludeMemberCount:   c.Params.IncludeMemberCount,
633		FilterAllowReference: c.Params.FilterAllowReference,
634	}
635	if c.Params.Paginate == nil || *c.Params.Paginate {
636		opts.PageOpts = &model.PageOpts{Page: c.Params.Page, PerPage: c.Params.PerPage}
637	}
638
639	groups, totalCount, err := c.App.GetGroupsByChannel(c.Params.ChannelId, opts)
640	if err != nil {
641		c.Err = err
642		return
643	}
644
645	b, marshalErr := json.Marshal(struct {
646		Groups []*model.GroupWithSchemeAdmin `json:"groups"`
647		Count  int                           `json:"total_group_count"`
648	}{
649		Groups: groups,
650		Count:  totalCount,
651	})
652
653	if marshalErr != nil {
654		c.Err = model.NewAppError("Api4.getGroupsByChannel", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
655		return
656	}
657
658	w.Write(b)
659}
660
661func getGroupsByTeam(c *Context, w http.ResponseWriter, r *http.Request) {
662	c.RequireTeamId()
663	if c.Err != nil {
664		return
665	}
666
667	if c.App.Srv().License() == nil || !*c.App.Srv().License().Features.LDAPGroups {
668		c.Err = model.NewAppError("Api4.getGroupsByTeam", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
669		return
670	}
671
672	opts := model.GroupSearchOpts{
673		Q:                    c.Params.Q,
674		IncludeMemberCount:   c.Params.IncludeMemberCount,
675		FilterAllowReference: c.Params.FilterAllowReference,
676	}
677	if c.Params.Paginate == nil || *c.Params.Paginate {
678		opts.PageOpts = &model.PageOpts{Page: c.Params.Page, PerPage: c.Params.PerPage}
679	}
680
681	groups, totalCount, err := c.App.GetGroupsByTeam(c.Params.TeamId, opts)
682	if err != nil {
683		c.Err = err
684		return
685	}
686
687	b, marshalErr := json.Marshal(struct {
688		Groups []*model.GroupWithSchemeAdmin `json:"groups"`
689		Count  int                           `json:"total_group_count"`
690	}{
691		Groups: groups,
692		Count:  totalCount,
693	})
694
695	if marshalErr != nil {
696		c.Err = model.NewAppError("Api4.getGroupsByTeam", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
697		return
698	}
699
700	w.Write(b)
701}
702
703func getGroupsAssociatedToChannelsByTeam(c *Context, w http.ResponseWriter, r *http.Request) {
704	c.RequireTeamId()
705	if c.Err != nil {
706		return
707	}
708
709	if c.App.Srv().License() == nil || !*c.App.Srv().License().Features.LDAPGroups {
710		c.Err = model.NewAppError("Api4.getGroupsAssociatedToChannelsByTeam", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
711		return
712	}
713
714	opts := model.GroupSearchOpts{
715		Q:                    c.Params.Q,
716		IncludeMemberCount:   c.Params.IncludeMemberCount,
717		FilterAllowReference: c.Params.FilterAllowReference,
718	}
719	if c.Params.Paginate == nil || *c.Params.Paginate {
720		opts.PageOpts = &model.PageOpts{Page: c.Params.Page, PerPage: c.Params.PerPage}
721	}
722
723	groupsAssociatedByChannelID, err := c.App.GetGroupsAssociatedToChannelsByTeam(c.Params.TeamId, opts)
724	if err != nil {
725		c.Err = err
726		return
727	}
728
729	b, marshalErr := json.Marshal(struct {
730		GroupsAssociatedToChannels map[string][]*model.GroupWithSchemeAdmin `json:"groups"`
731	}{
732		GroupsAssociatedToChannels: groupsAssociatedByChannelID,
733	})
734
735	if marshalErr != nil {
736		c.Err = model.NewAppError("Api4.getGroupsAssociatedToChannelsByTeam", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
737		return
738	}
739
740	w.Write(b)
741}
742
743func getGroups(c *Context, w http.ResponseWriter, r *http.Request) {
744	if c.App.Srv().License() == nil || !*c.App.Srv().License().Features.LDAPGroups {
745		c.Err = model.NewAppError("Api4.getGroups", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
746		return
747	}
748	var teamID, channelID string
749
750	if id := c.Params.NotAssociatedToTeam; model.IsValidId(id) {
751		teamID = id
752	}
753
754	if id := c.Params.NotAssociatedToChannel; model.IsValidId(id) {
755		channelID = id
756	}
757
758	opts := model.GroupSearchOpts{
759		Q:                         c.Params.Q,
760		IncludeMemberCount:        c.Params.IncludeMemberCount,
761		FilterAllowReference:      c.Params.FilterAllowReference,
762		FilterParentTeamPermitted: c.Params.FilterParentTeamPermitted,
763	}
764
765	if teamID != "" {
766		_, err := c.App.GetTeam(teamID)
767		if err != nil {
768			c.Err = err
769			return
770		}
771
772		opts.NotAssociatedToTeam = teamID
773	}
774
775	if channelID != "" {
776		channel, err := c.App.GetChannel(channelID)
777		if err != nil {
778			c.Err = err
779			return
780		}
781		var permission *model.Permission
782		if channel.Type == model.ChannelTypePrivate {
783			permission = model.PermissionManagePrivateChannelMembers
784		} else {
785			permission = model.PermissionManagePublicChannelMembers
786		}
787		if !c.App.SessionHasPermissionToChannel(*c.AppContext.Session(), channelID, permission) {
788			c.SetPermissionError(permission)
789			return
790		}
791		opts.NotAssociatedToChannel = channelID
792	}
793
794	sinceString := r.URL.Query().Get("since")
795	if sinceString != "" {
796		since, parseError := strconv.ParseInt(sinceString, 10, 64)
797		if parseError != nil {
798			c.SetInvalidParam("since")
799			return
800		}
801		opts.Since = since
802	}
803
804	groups, err := c.App.GetGroups(c.Params.Page, c.Params.PerPage, opts)
805	if err != nil {
806		c.Err = err
807		return
808	}
809
810	b, marshalErr := json.Marshal(groups)
811	if marshalErr != nil {
812		c.Err = model.NewAppError("Api4.getGroups", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
813		return
814	}
815
816	w.Write(b)
817}
818