github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/api4/ldap.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package api4 5 6 import ( 7 "encoding/json" 8 "mime/multipart" 9 "net/http" 10 11 "github.com/masterhung0112/hk_server/v5/audit" 12 "github.com/masterhung0112/hk_server/v5/model" 13 ) 14 15 type mixedUnlinkedGroup struct { 16 Id *string `json:"mattermost_group_id"` 17 DisplayName string `json:"name"` 18 RemoteId string `json:"primary_key"` 19 HasSyncables *bool `json:"has_syncables"` 20 } 21 22 func (api *API) InitLdap() { 23 api.BaseRoutes.LDAP.Handle("/sync", api.ApiSessionRequired(syncLdap)).Methods("POST") 24 api.BaseRoutes.LDAP.Handle("/test", api.ApiSessionRequired(testLdap)).Methods("POST") 25 api.BaseRoutes.LDAP.Handle("/migrateid", api.ApiSessionRequired(migrateIdLdap)).Methods("POST") 26 27 // GET /api/v4/ldap/groups?page=0&per_page=1000 28 api.BaseRoutes.LDAP.Handle("/groups", api.ApiSessionRequired(getLdapGroups)).Methods("GET") 29 30 // POST /api/v4/ldap/groups/:remote_id/link 31 api.BaseRoutes.LDAP.Handle(`/groups/{remote_id}/link`, api.ApiSessionRequired(linkLdapGroup)).Methods("POST") 32 33 // DELETE /api/v4/ldap/groups/:remote_id/link 34 api.BaseRoutes.LDAP.Handle(`/groups/{remote_id}/link`, api.ApiSessionRequired(unlinkLdapGroup)).Methods("DELETE") 35 36 api.BaseRoutes.LDAP.Handle("/certificate/public", api.ApiSessionRequired(addLdapPublicCertificate)).Methods("POST") 37 api.BaseRoutes.LDAP.Handle("/certificate/private", api.ApiSessionRequired(addLdapPrivateCertificate)).Methods("POST") 38 39 api.BaseRoutes.LDAP.Handle("/certificate/public", api.ApiSessionRequired(removeLdapPublicCertificate)).Methods("DELETE") 40 api.BaseRoutes.LDAP.Handle("/certificate/private", api.ApiSessionRequired(removeLdapPrivateCertificate)).Methods("DELETE") 41 42 } 43 44 func syncLdap(c *Context, w http.ResponseWriter, r *http.Request) { 45 if c.App.Srv().License() == nil || !*c.App.Srv().License().Features.LDAP { 46 c.Err = model.NewAppError("Api4.syncLdap", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented) 47 return 48 } 49 50 type LdapSyncOptions struct { 51 IncludeRemovedMembers bool `json:"include_removed_members"` 52 } 53 var opts LdapSyncOptions 54 json.NewDecoder(r.Body).Decode(&opts) 55 56 auditRec := c.MakeAuditRecord("syncLdap", audit.Fail) 57 defer c.LogAuditRec(auditRec) 58 59 if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_CREATE_LDAP_SYNC_JOB) { 60 c.SetPermissionError(model.PERMISSION_CREATE_LDAP_SYNC_JOB) 61 return 62 } 63 64 c.App.SyncLdap(opts.IncludeRemovedMembers) 65 66 auditRec.Success() 67 ReturnStatusOK(w) 68 } 69 70 func testLdap(c *Context, w http.ResponseWriter, r *http.Request) { 71 if c.App.Srv().License() == nil || !*c.App.Srv().License().Features.LDAP { 72 c.Err = model.NewAppError("Api4.testLdap", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented) 73 return 74 } 75 76 if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_TEST_LDAP) { 77 c.SetPermissionError(model.PERMISSION_TEST_LDAP) 78 return 79 } 80 81 if err := c.App.TestLdap(); err != nil { 82 c.Err = err 83 return 84 } 85 86 ReturnStatusOK(w) 87 } 88 89 func getLdapGroups(c *Context, w http.ResponseWriter, r *http.Request) { 90 if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_GROUPS) { 91 c.SetPermissionError(model.PERMISSION_SYSCONSOLE_READ_USERMANAGEMENT_GROUPS) 92 return 93 } 94 95 if c.App.Srv().License() == nil || !*c.App.Srv().License().Features.LDAPGroups { 96 c.Err = model.NewAppError("Api4.getLdapGroups", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented) 97 return 98 } 99 100 opts := model.LdapGroupSearchOpts{ 101 Q: c.Params.Q, 102 } 103 if c.Params.IsLinked != nil { 104 opts.IsLinked = c.Params.IsLinked 105 } 106 if c.Params.IsConfigured != nil { 107 opts.IsConfigured = c.Params.IsConfigured 108 } 109 110 groups, total, err := c.App.GetAllLdapGroupsPage(c.Params.Page, c.Params.PerPage, opts) 111 if err != nil { 112 c.Err = err 113 return 114 } 115 116 mugs := []*mixedUnlinkedGroup{} 117 for _, group := range groups { 118 mug := &mixedUnlinkedGroup{ 119 DisplayName: group.DisplayName, 120 RemoteId: group.RemoteId, 121 } 122 if len(group.Id) == 26 { 123 mug.Id = &group.Id 124 mug.HasSyncables = &group.HasSyncables 125 } 126 mugs = append(mugs, mug) 127 } 128 129 b, marshalErr := json.Marshal(struct { 130 Count int `json:"count"` 131 Groups []*mixedUnlinkedGroup `json:"groups"` 132 }{Count: total, Groups: mugs}) 133 if marshalErr != nil { 134 c.Err = model.NewAppError("Api4.getLdapGroups", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError) 135 return 136 } 137 138 w.Write(b) 139 } 140 141 func linkLdapGroup(c *Context, w http.ResponseWriter, r *http.Request) { 142 c.RequireRemoteId() 143 if c.Err != nil { 144 return 145 } 146 147 if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_GROUPS) { 148 c.SetPermissionError(model.PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_GROUPS) 149 return 150 } 151 152 auditRec := c.MakeAuditRecord("linkLdapGroup", audit.Fail) 153 defer c.LogAuditRec(auditRec) 154 auditRec.AddMeta("remote_id", c.Params.RemoteId) 155 156 if c.App.Srv().License() == nil || !*c.App.Srv().License().Features.LDAPGroups { 157 c.Err = model.NewAppError("Api4.linkLdapGroup", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented) 158 return 159 } 160 161 ldapGroup, err := c.App.GetLdapGroup(c.Params.RemoteId) 162 if err != nil { 163 c.Err = err 164 return 165 } 166 auditRec.AddMeta("ldap_group", ldapGroup) 167 168 if ldapGroup == nil { 169 c.Err = model.NewAppError("Api4.linkLdapGroup", "api.ldap_group.not_found", nil, "", http.StatusNotFound) 170 return 171 } 172 173 group, err := c.App.GetGroupByRemoteID(ldapGroup.RemoteId, model.GroupSourceLdap) 174 if err != nil && err.Id != "app.group.no_rows" { 175 c.Err = err 176 return 177 } 178 if group != nil { 179 auditRec.AddMeta("group", group) 180 } 181 182 var status int 183 var newOrUpdatedGroup *model.Group 184 185 // Truncate display name if necessary 186 var displayName string 187 if len(ldapGroup.DisplayName) > model.GroupDisplayNameMaxLength { 188 displayName = ldapGroup.DisplayName[:model.GroupDisplayNameMaxLength] 189 } else { 190 displayName = ldapGroup.DisplayName 191 } 192 193 // Group has been previously linked 194 if group != nil { 195 if group.DeleteAt == 0 { 196 newOrUpdatedGroup = group 197 } else { 198 group.DeleteAt = 0 199 group.DisplayName = displayName 200 group.RemoteId = ldapGroup.RemoteId 201 newOrUpdatedGroup, err = c.App.UpdateGroup(group) 202 if err != nil { 203 c.Err = err 204 return 205 } 206 } 207 status = http.StatusOK 208 } else { 209 // Group has never been linked 210 // 211 // For group mentions implementation, the Name column will no longer be set by default. 212 // Instead it will be set and saved in the web app when Group Mentions is enabled. 213 newGroup := &model.Group{ 214 DisplayName: displayName, 215 RemoteId: ldapGroup.RemoteId, 216 Source: model.GroupSourceLdap, 217 } 218 newOrUpdatedGroup, err = c.App.CreateGroup(newGroup) 219 if err != nil { 220 c.Err = err 221 return 222 } 223 status = http.StatusCreated 224 } 225 226 b, marshalErr := json.Marshal(newOrUpdatedGroup) 227 if marshalErr != nil { 228 c.Err = model.NewAppError("Api4.linkLdapGroup", "api.marshal_error", nil, marshalErr.Error(), http.StatusInternalServerError) 229 return 230 } 231 232 auditRec.Success() 233 234 w.WriteHeader(status) 235 w.Write(b) 236 } 237 238 func unlinkLdapGroup(c *Context, w http.ResponseWriter, r *http.Request) { 239 c.RequireRemoteId() 240 if c.Err != nil { 241 return 242 } 243 244 auditRec := c.MakeAuditRecord("unlinkLdapGroup", audit.Fail) 245 defer c.LogAuditRec(auditRec) 246 auditRec.AddMeta("remote_id", c.Params.RemoteId) 247 248 if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_GROUPS) { 249 c.SetPermissionError(model.PERMISSION_SYSCONSOLE_WRITE_USERMANAGEMENT_GROUPS) 250 return 251 } 252 253 if c.App.Srv().License() == nil || !*c.App.Srv().License().Features.LDAPGroups { 254 c.Err = model.NewAppError("Api4.unlinkLdapGroup", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented) 255 return 256 } 257 258 group, err := c.App.GetGroupByRemoteID(c.Params.RemoteId, model.GroupSourceLdap) 259 if err != nil { 260 c.Err = err 261 return 262 } 263 auditRec.AddMeta("group", group) 264 265 if group.DeleteAt == 0 { 266 _, err = c.App.DeleteGroup(group.Id) 267 if err != nil { 268 c.Err = err 269 return 270 } 271 } 272 273 auditRec.Success() 274 ReturnStatusOK(w) 275 } 276 277 func migrateIdLdap(c *Context, w http.ResponseWriter, r *http.Request) { 278 props := model.StringInterfaceFromJson(r.Body) 279 toAttribute, ok := props["toAttribute"].(string) 280 if !ok || toAttribute == "" { 281 c.SetInvalidParam("toAttribute") 282 return 283 } 284 285 auditRec := c.MakeAuditRecord("idMigrateLdap", audit.Fail) 286 defer c.LogAuditRec(auditRec) 287 288 if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_MANAGE_SYSTEM) { 289 c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM) 290 return 291 } 292 293 if c.App.Srv().License() == nil || !*c.App.Srv().License().Features.LDAP { 294 c.Err = model.NewAppError("Api4.idMigrateLdap", "api.ldap_groups.license_error", nil, "", http.StatusNotImplemented) 295 return 296 } 297 298 if err := c.App.MigrateIdLDAP(toAttribute); err != nil { 299 c.Err = err 300 return 301 } 302 303 auditRec.Success() 304 ReturnStatusOK(w) 305 } 306 307 func parseLdapCertificateRequest(r *http.Request, maxFileSize int64) (*multipart.FileHeader, *model.AppError) { 308 err := r.ParseMultipartForm(maxFileSize) 309 if err != nil { 310 return nil, model.NewAppError("addLdapCertificate", "api.admin.add_certificate.parseform.app_error", nil, err.Error(), http.StatusBadRequest) 311 } 312 313 m := r.MultipartForm 314 315 fileArray, ok := m.File["certificate"] 316 if !ok { 317 return nil, model.NewAppError("addLdapCertificate", "api.admin.add_certificate.no_file.app_error", nil, "", http.StatusBadRequest) 318 } 319 320 if len(fileArray) <= 0 { 321 return nil, model.NewAppError("addLdapCertificate", "api.admin.add_certificate.array.app_error", nil, "", http.StatusBadRequest) 322 } 323 324 return fileArray[0], nil 325 } 326 327 func addLdapPublicCertificate(c *Context, w http.ResponseWriter, r *http.Request) { 328 if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_ADD_LDAP_PUBLIC_CERT) { 329 c.SetPermissionError(model.PERMISSION_ADD_LDAP_PUBLIC_CERT) 330 return 331 } 332 333 fileData, err := parseLdapCertificateRequest(r, *c.App.Config().FileSettings.MaxFileSize) 334 if err != nil { 335 c.Err = err 336 return 337 } 338 339 auditRec := c.MakeAuditRecord("addLdapPublicCertificate", audit.Fail) 340 defer c.LogAuditRec(auditRec) 341 auditRec.AddMeta("filename", fileData.Filename) 342 343 if err := c.App.AddLdapPublicCertificate(fileData); err != nil { 344 c.Err = err 345 return 346 } 347 auditRec.Success() 348 ReturnStatusOK(w) 349 } 350 351 func addLdapPrivateCertificate(c *Context, w http.ResponseWriter, r *http.Request) { 352 if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_ADD_LDAP_PRIVATE_CERT) { 353 c.SetPermissionError(model.PERMISSION_ADD_LDAP_PRIVATE_CERT) 354 return 355 } 356 357 fileData, err := parseLdapCertificateRequest(r, *c.App.Config().FileSettings.MaxFileSize) 358 if err != nil { 359 c.Err = err 360 return 361 } 362 363 auditRec := c.MakeAuditRecord("addLdapPrivateCertificate", audit.Fail) 364 defer c.LogAuditRec(auditRec) 365 auditRec.AddMeta("filename", fileData.Filename) 366 367 if err := c.App.AddLdapPrivateCertificate(fileData); err != nil { 368 c.Err = err 369 return 370 } 371 auditRec.Success() 372 ReturnStatusOK(w) 373 } 374 375 func removeLdapPublicCertificate(c *Context, w http.ResponseWriter, r *http.Request) { 376 if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_REMOVE_LDAP_PUBLIC_CERT) { 377 c.SetPermissionError(model.PERMISSION_REMOVE_LDAP_PUBLIC_CERT) 378 return 379 } 380 381 auditRec := c.MakeAuditRecord("removeLdapPublicCertificate", audit.Fail) 382 defer c.LogAuditRec(auditRec) 383 384 if err := c.App.RemoveLdapPublicCertificate(); err != nil { 385 c.Err = err 386 return 387 } 388 389 auditRec.Success() 390 ReturnStatusOK(w) 391 } 392 393 func removeLdapPrivateCertificate(c *Context, w http.ResponseWriter, r *http.Request) { 394 if !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PERMISSION_REMOVE_LDAP_PRIVATE_CERT) { 395 c.SetPermissionError(model.PERMISSION_REMOVE_LDAP_PRIVATE_CERT) 396 return 397 } 398 399 auditRec := c.MakeAuditRecord("removeLdapPrivateCertificate", audit.Fail) 400 defer c.LogAuditRec(auditRec) 401 402 if err := c.App.RemoveLdapPrivateCertificate(); err != nil { 403 c.Err = err 404 return 405 } 406 407 auditRec.Success() 408 ReturnStatusOK(w) 409 }