github.com/xzl8028/xenia-server@v0.0.0-20190809101854-18450a97da63/api4/ldap.go (about)

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