github.com/mad-app/mattermost-server@v5.11.1+incompatible/api4/ldap.go (about)

     1  // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package api4
     5  
     6  import (
     7  	"database/sql"
     8  	"encoding/json"
     9  	"net/http"
    10  
    11  	"github.com/mattermost/mattermost-server/model"
    12  )
    13  
    14  type mixedUnlinkedGroup struct {
    15  	Id           *string `json:"mattermost_group_id"`
    16  	DisplayName  string  `json:"name"`
    17  	RemoteId     string  `json:"primary_key"`
    18  	HasSyncables *bool   `json:"has_syncables"`
    19  }
    20  
    21  func (api *API) InitLdap() {
    22  	api.BaseRoutes.LDAP.Handle("/sync", api.ApiSessionRequired(syncLdap)).Methods("POST")
    23  	api.BaseRoutes.LDAP.Handle("/test", api.ApiSessionRequired(testLdap)).Methods("POST")
    24  
    25  	// GET /api/v4/ldap/groups?page=0&per_page=1000
    26  	api.BaseRoutes.LDAP.Handle("/groups", api.ApiSessionRequired(getLdapGroups)).Methods("GET")
    27  
    28  	// POST /api/v4/ldap/groups/:remote_id/link
    29  	api.BaseRoutes.LDAP.Handle(`/groups/{remote_id}/link`, api.ApiSessionRequired(linkLdapGroup)).Methods("POST")
    30  
    31  	// DELETE /api/v4/ldap/groups/:remote_id/link
    32  	api.BaseRoutes.LDAP.Handle(`/groups/{remote_id}/link`, api.ApiSessionRequired(unlinkLdapGroup)).Methods("DELETE")
    33  }
    34  
    35  func syncLdap(c *Context, w http.ResponseWriter, r *http.Request) {
    36  	if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
    37  		c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
    38  		return
    39  	}
    40  
    41  	c.App.SyncLdap()
    42  
    43  	ReturnStatusOK(w)
    44  }
    45  
    46  func testLdap(c *Context, w http.ResponseWriter, r *http.Request) {
    47  	if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
    48  		c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
    49  		return
    50  	}
    51  
    52  	if err := c.App.TestLdap(); err != nil {
    53  		c.Err = err
    54  		return
    55  	}
    56  
    57  	ReturnStatusOK(w)
    58  }
    59  
    60  func getLdapGroups(c *Context, w http.ResponseWriter, r *http.Request) {
    61  	if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
    62  		c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
    63  		return
    64  	}
    65  
    66  	if c.App.License() == nil || !*c.App.License().Features.LDAPGroups {
    67  		c.Err = model.NewAppError("Api4.getLdapGroups", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
    68  		return
    69  	}
    70  
    71  	opts := model.GroupSearchOpts{
    72  		Q: c.Params.Q,
    73  	}
    74  	if c.Params.IsLinked != nil {
    75  		opts.IsLinked = c.Params.IsLinked
    76  	}
    77  	if c.Params.IsConfigured != nil {
    78  		opts.IsConfigured = c.Params.IsConfigured
    79  	}
    80  
    81  	groups, total, err := c.App.GetAllLdapGroupsPage(c.Params.Page, c.Params.PerPage, opts)
    82  	if err != nil {
    83  		c.Err = err
    84  		return
    85  	}
    86  
    87  	mugs := []*mixedUnlinkedGroup{}
    88  	for _, group := range groups {
    89  		mug := &mixedUnlinkedGroup{
    90  			DisplayName: group.DisplayName,
    91  			RemoteId:    group.RemoteId,
    92  		}
    93  		if len(group.Id) == 26 {
    94  			mug.Id = &group.Id
    95  			mug.HasSyncables = &group.HasSyncables
    96  		}
    97  		mugs = append(mugs, mug)
    98  	}
    99  
   100  	b, marshalErr := json.Marshal(struct {
   101  		Count  int                   `json:"count"`
   102  		Groups []*mixedUnlinkedGroup `json:"groups"`
   103  	}{Count: total, Groups: mugs})
   104  	if marshalErr != nil {
   105  		c.Err = model.NewAppError("Api4.getLdapGroups", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
   106  		return
   107  	}
   108  
   109  	w.Write(b)
   110  }
   111  
   112  func linkLdapGroup(c *Context, w http.ResponseWriter, r *http.Request) {
   113  	c.RequireRemoteId()
   114  	if c.Err != nil {
   115  		return
   116  	}
   117  
   118  	if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
   119  		c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
   120  		return
   121  	}
   122  
   123  	if c.App.License() == nil || !*c.App.License().Features.LDAPGroups {
   124  		c.Err = model.NewAppError("Api4.linkLdapGroup", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
   125  		return
   126  	}
   127  
   128  	ldapGroup, err := c.App.GetLdapGroup(c.Params.RemoteId)
   129  	if err != nil {
   130  		c.Err = err
   131  		return
   132  	}
   133  
   134  	if ldapGroup == nil {
   135  		c.Err = model.NewAppError("Api4.linkLdapGroup", "api.ldap_group.not_found", nil, "", http.StatusNotFound)
   136  		return
   137  	}
   138  
   139  	group, err := c.App.GetGroupByRemoteID(ldapGroup.RemoteId, model.GroupSourceLdap)
   140  	if err != nil && err.DetailedError != sql.ErrNoRows.Error() {
   141  		c.Err = err
   142  		return
   143  	}
   144  
   145  	var status int
   146  	var newOrUpdatedGroup *model.Group
   147  
   148  	// Group has been previously linked
   149  	if group != nil {
   150  		if group.DeleteAt == 0 {
   151  			newOrUpdatedGroup = group
   152  		} else {
   153  			group.DeleteAt = 0
   154  			group.DisplayName = ldapGroup.DisplayName
   155  			group.RemoteId = ldapGroup.RemoteId
   156  			newOrUpdatedGroup, err = c.App.UpdateGroup(group)
   157  			if err != nil {
   158  				c.Err = err
   159  				return
   160  			}
   161  		}
   162  		status = http.StatusOK
   163  	} else {
   164  		// Group has never been linked
   165  		//
   166  		// TODO: In a future phase of LDAP groups sync `Name` will be used for at-mentions and will be editable on
   167  		// the front-end so it will not have an initial value of `model.NewId()` but rather a slugified version of
   168  		// the LDAP group name with an appended duplicate-breaker.
   169  		newGroup := &model.Group{
   170  			Name:        model.NewId(),
   171  			DisplayName: ldapGroup.DisplayName,
   172  			RemoteId:    ldapGroup.RemoteId,
   173  			Source:      model.GroupSourceLdap,
   174  		}
   175  		newOrUpdatedGroup, err = c.App.CreateGroup(newGroup)
   176  		if err != nil {
   177  			c.Err = err
   178  			return
   179  		}
   180  		status = http.StatusCreated
   181  	}
   182  
   183  	b, marshalErr := json.Marshal(newOrUpdatedGroup)
   184  	if marshalErr != nil {
   185  		c.Err = model.NewAppError("Api4.linkLdapGroup", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError)
   186  		return
   187  	}
   188  
   189  	w.WriteHeader(status)
   190  	w.Write(b)
   191  }
   192  
   193  func unlinkLdapGroup(c *Context, w http.ResponseWriter, r *http.Request) {
   194  	c.RequireRemoteId()
   195  	if c.Err != nil {
   196  		return
   197  	}
   198  
   199  	if !c.App.SessionHasPermissionTo(c.App.Session, model.PERMISSION_MANAGE_SYSTEM) {
   200  		c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
   201  		return
   202  	}
   203  
   204  	if c.App.License() == nil || !*c.App.License().Features.LDAPGroups {
   205  		c.Err = model.NewAppError("Api4.unlinkLdapGroup", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented)
   206  		return
   207  	}
   208  
   209  	group, err := c.App.GetGroupByRemoteID(c.Params.RemoteId, model.GroupSourceLdap)
   210  	if err != nil {
   211  		c.Err = err
   212  		return
   213  	}
   214  
   215  	if group.DeleteAt == 0 {
   216  		_, err = c.App.DeleteGroup(group.Id)
   217  		if err != nil {
   218  			c.Err = err
   219  			return
   220  		}
   221  	}
   222  
   223  	ReturnStatusOK(w)
   224  }