ldap.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. package ldap
  2. import (
  3. "encoding/json"
  4. "log"
  5. "net/http"
  6. "regexp"
  7. "strconv"
  8. "github.com/go-ldap/ldap"
  9. auth "imuslab.com/arozos/mod/auth"
  10. "imuslab.com/arozos/mod/auth/ldap/ldapreader"
  11. "imuslab.com/arozos/mod/auth/oauth2/syncdb"
  12. reg "imuslab.com/arozos/mod/auth/register"
  13. "imuslab.com/arozos/mod/common"
  14. db "imuslab.com/arozos/mod/database"
  15. permission "imuslab.com/arozos/mod/permission"
  16. "imuslab.com/arozos/mod/time/nightly"
  17. "imuslab.com/arozos/mod/user"
  18. )
  19. type ldapHandler struct {
  20. ag *auth.AuthAgent
  21. ldapreader *ldapreader.LdapReader
  22. reg *reg.RegisterHandler
  23. coredb *db.Database
  24. permissionHandler *permission.PermissionHandler
  25. userHandler *user.UserHandler
  26. iconSystem string
  27. syncdb *syncdb.SyncDB
  28. nightlyManager *nightly.TaskManager
  29. }
  30. type Config struct {
  31. Enabled bool `json:"enabled"`
  32. BindUsername string `json:"bind_username"`
  33. BindPassword string `json:"bind_password"`
  34. FQDN string `json:"fqdn"`
  35. BaseDN string `json:"base_dn"`
  36. }
  37. type UserAccount struct {
  38. Username string `json:"username"`
  39. Group []string `json:"group"`
  40. EquivGroup []string `json:"equiv_group"`
  41. }
  42. //syncorizeUserReturnInterface not designed to be used outside
  43. type syncorizeUserReturnInterface struct {
  44. Userinfo []UserAccount `json:"userinfo"`
  45. Length int `json:"length"`
  46. Error string `json:"error"`
  47. }
  48. //NewLdapHandler xxx
  49. func NewLdapHandler(authAgent *auth.AuthAgent, register *reg.RegisterHandler, coreDb *db.Database, permissionHandler *permission.PermissionHandler, userHandler *user.UserHandler, nightlyManager *nightly.TaskManager, iconSystem string) *ldapHandler {
  50. //ldap handler init
  51. log.Println("Starting LDAP client...")
  52. err := coreDb.NewTable("ldap")
  53. if err != nil {
  54. log.Println("Failed to create LDAP database. Terminating.")
  55. panic(err)
  56. }
  57. //key value to be used for LDAP authentication
  58. BindUsername := readSingleConfig("BindUsername", coreDb)
  59. BindPassword := readSingleConfig("BindPassword", coreDb)
  60. FQDN := readSingleConfig("FQDN", coreDb)
  61. BaseDN := readSingleConfig("BaseDN", coreDb)
  62. LDAPHandler := ldapHandler{
  63. ag: authAgent,
  64. ldapreader: ldapreader.NewLDAPReader(BindUsername, BindPassword, FQDN, BaseDN),
  65. reg: register,
  66. coredb: coreDb,
  67. permissionHandler: permissionHandler,
  68. userHandler: userHandler,
  69. iconSystem: iconSystem,
  70. syncdb: syncdb.NewSyncDB(),
  71. nightlyManager: nightlyManager,
  72. }
  73. nightlyManager.RegisterNightlyTask(LDAPHandler.NightlySync)
  74. return &LDAPHandler
  75. }
  76. func (ldap *ldapHandler) ReadConfig(w http.ResponseWriter, r *http.Request) {
  77. //basic components
  78. enabled, err := strconv.ParseBool(ldap.readSingleConfig("enabled"))
  79. if err != nil {
  80. common.SendTextResponse(w, "Invalid config value [key=enabled].")
  81. return
  82. }
  83. //get the LDAP config from db
  84. BindUsername := ldap.readSingleConfig("BindUsername")
  85. BindPassword := ldap.readSingleConfig("BindPassword")
  86. FQDN := ldap.readSingleConfig("FQDN")
  87. BaseDN := ldap.readSingleConfig("BaseDN")
  88. //marshall it and return
  89. config, err := json.Marshal(Config{
  90. Enabled: enabled,
  91. BindUsername: BindUsername,
  92. BindPassword: BindPassword,
  93. FQDN: FQDN,
  94. BaseDN: BaseDN,
  95. })
  96. if err != nil {
  97. empty, err := json.Marshal(Config{})
  98. if err != nil {
  99. common.SendErrorResponse(w, "Error while marshalling config")
  100. }
  101. common.SendJSONResponse(w, string(empty))
  102. }
  103. common.SendJSONResponse(w, string(config))
  104. }
  105. func (ldap *ldapHandler) WriteConfig(w http.ResponseWriter, r *http.Request) {
  106. //receive the parameter
  107. enabled, err := common.Mv(r, "enabled", true)
  108. if err != nil {
  109. common.SendErrorResponse(w, "enabled field can't be empty")
  110. return
  111. }
  112. //allow empty fields if enabled is false
  113. showError := true
  114. if enabled != "true" {
  115. showError = false
  116. }
  117. //four fields to store the LDAP authentication information
  118. BindUsername, err := common.Mv(r, "bind_username", true)
  119. if err != nil {
  120. if showError {
  121. common.SendErrorResponse(w, "bind_username field can't be empty")
  122. return
  123. }
  124. }
  125. BindPassword, err := common.Mv(r, "bind_password", true)
  126. if err != nil {
  127. if showError {
  128. common.SendErrorResponse(w, "bind_password field can't be empty")
  129. return
  130. }
  131. }
  132. FQDN, err := common.Mv(r, "fqdn", true)
  133. if err != nil {
  134. if showError {
  135. common.SendErrorResponse(w, "fqdn field can't be empty")
  136. return
  137. }
  138. }
  139. BaseDN, err := common.Mv(r, "base_dn", true)
  140. if err != nil {
  141. if showError {
  142. common.SendErrorResponse(w, "base_dn field can't be empty")
  143. return
  144. }
  145. }
  146. //write the data back to db
  147. ldap.coredb.Write("ldap", "enabled", enabled)
  148. ldap.coredb.Write("ldap", "BindUsername", BindUsername)
  149. ldap.coredb.Write("ldap", "BindPassword", BindPassword)
  150. ldap.coredb.Write("ldap", "FQDN", FQDN)
  151. ldap.coredb.Write("ldap", "BaseDN", BaseDN)
  152. //update the new authencation infromation
  153. ldap.ldapreader = ldapreader.NewLDAPReader(BindUsername, BindPassword, FQDN, BaseDN)
  154. //return ok
  155. common.SendOK(w)
  156. }
  157. //@para limit: -1 means unlimited
  158. func (ldap *ldapHandler) getAllUser(limit int) ([]UserAccount, error) {
  159. //read the user account from ldap, if limit is -1 then it will read all USERS
  160. var accounts []UserAccount
  161. result, err := ldap.ldapreader.GetAllUser()
  162. if err != nil {
  163. return []UserAccount{}, err
  164. }
  165. //loop through the result
  166. for i, v := range result {
  167. account := ldap.convertGroup(v)
  168. accounts = append(accounts, account)
  169. if i > limit && limit != -1 {
  170. break
  171. }
  172. }
  173. //check if the return struct is empty, if yes then insert empty
  174. if len(accounts) > 0 {
  175. return accounts[1:], nil
  176. } else {
  177. return []UserAccount{}, nil
  178. }
  179. }
  180. func (ldap *ldapHandler) convertGroup(ldapUser *ldap.Entry) UserAccount {
  181. //check the group belongs
  182. var Group []string
  183. var EquivGroup []string
  184. regexSyntax := regexp.MustCompile("cn=([^,]+),")
  185. for _, v := range ldapUser.GetAttributeValues("memberOf") {
  186. groups := regexSyntax.FindStringSubmatch(v)
  187. if len(groups) > 0 {
  188. //check if the LDAP group is already exists in ArOZOS system
  189. if ldap.permissionHandler.GroupExists(groups[1]) {
  190. EquivGroup = append(EquivGroup, groups[1])
  191. }
  192. //LDAP list
  193. Group = append(Group, groups[1])
  194. }
  195. }
  196. if len(EquivGroup) < 1 {
  197. if !ldap.permissionHandler.GroupExists(ldap.reg.GetDefaultUserGroup()) {
  198. //create new user group named default, prventing user don't have a group
  199. ldap.permissionHandler.NewPermissionGroup("default", false, 15<<30, []string{}, "Desktop")
  200. ldap.reg.SetDefaultUserGroup("default")
  201. }
  202. EquivGroup = append(EquivGroup, ldap.reg.GetDefaultUserGroup())
  203. }
  204. account := UserAccount{
  205. Username: ldapUser.GetAttributeValue("cn"),
  206. Group: Group,
  207. EquivGroup: EquivGroup,
  208. }
  209. return account
  210. }
  211. func (ldap *ldapHandler) TestConnection(w http.ResponseWriter, r *http.Request) {
  212. //marshall it and return the connection status
  213. userList, err := ldap.getAllUser(10)
  214. if err != nil {
  215. errMessage, err := json.Marshal(syncorizeUserReturnInterface{Error: err.Error()})
  216. if err != nil {
  217. common.SendErrorResponse(w, "{\"error\":\"Error while marshalling information\"}")
  218. return
  219. }
  220. common.SendJSONResponse(w, string(errMessage))
  221. return
  222. }
  223. returnJSON := syncorizeUserReturnInterface{Userinfo: userList, Length: len(userList), Error: ""}
  224. accountJSON, err := json.Marshal(returnJSON)
  225. if err != nil {
  226. errMessage, err := json.Marshal(syncorizeUserReturnInterface{Error: err.Error()})
  227. if err != nil {
  228. common.SendErrorResponse(w, "{\"error\":\"Error while marshalling information\"}")
  229. return
  230. }
  231. common.SendJSONResponse(w, string(errMessage))
  232. return
  233. }
  234. common.SendJSONResponse(w, string(accountJSON))
  235. }
  236. func (ldap *ldapHandler) checkCurrUserAdmin(w http.ResponseWriter, r *http.Request) bool {
  237. //check current user is admin and new update will remove it or not
  238. currentLoggedInUser, err := ldap.userHandler.GetUserInfoFromRequest(w, r)
  239. if err != nil {
  240. common.SendErrorResponse(w, "Error while getting user info")
  241. return false
  242. }
  243. ldapCurrUserInfo, err := ldap.ldapreader.GetUser(currentLoggedInUser.Username)
  244. if err != nil {
  245. common.SendErrorResponse(w, "Error while getting user info from LDAP")
  246. return false
  247. }
  248. isAdmin := false
  249. //get the croups out from LDAP group list
  250. regexSyntax := regexp.MustCompile("cn=([^,]+),")
  251. for _, v := range ldapCurrUserInfo.GetAttributeValues("memberOf") {
  252. //loop through all memberOf's array
  253. groups := regexSyntax.FindStringSubmatch(v)
  254. //if after regex there is still groups exists
  255. if len(groups) > 0 {
  256. //check if the LDAP group is already exists in ArOZOS system
  257. if ldap.permissionHandler.GroupExists(groups[1]) {
  258. if ldap.permissionHandler.GetPermissionGroupByName(groups[1]).IsAdmin {
  259. isAdmin = true
  260. }
  261. }
  262. }
  263. }
  264. return isAdmin
  265. }
  266. func (ldap *ldapHandler) SynchronizeUser(w http.ResponseWriter, r *http.Request) {
  267. //check if suer is admin before executing the command
  268. //if user is admin then check if user will lost him/her's admin access
  269. consistencyCheck := ldap.checkCurrUserAdmin(w, r)
  270. if !consistencyCheck {
  271. common.SendErrorResponse(w, "You will no longer become the admin after synchronizing, synchronize terminated")
  272. return
  273. }
  274. err := ldap.SynchronizeUserFromLDAP()
  275. if err != nil {
  276. common.SendErrorResponse(w, err.Error())
  277. return
  278. }
  279. common.SendOK(w)
  280. }
  281. func (ldap *ldapHandler) NightlySync() {
  282. err := ldap.SynchronizeUserFromLDAP()
  283. log.Println(err)
  284. }
  285. func (ldap *ldapHandler) SynchronizeUserFromLDAP() error {
  286. //check if suer is admin before executing the command
  287. //if user is admin then check if user will lost him/her's admin access
  288. ldapUsersList, err := ldap.getAllUser(-1)
  289. if err != nil {
  290. return err
  291. }
  292. for _, ldapUser := range ldapUsersList {
  293. //check if user exist in system
  294. if ldap.ag.UserExists(ldapUser.Username) {
  295. //if exists, then check if the user group is the same with ldap's setting
  296. //Get the permission groups by their ids
  297. userinfo, err := ldap.userHandler.GetUserInfoFromUsername(ldapUser.Username)
  298. if err != nil {
  299. return err
  300. }
  301. newPermissionGroups := ldap.permissionHandler.GetPermissionGroupByNameList(ldapUser.EquivGroup)
  302. //Set the user's permission to these groups
  303. userinfo.SetUserPermissionGroup(newPermissionGroups)
  304. }
  305. }
  306. return nil
  307. }
  308. //LOGIN related function
  309. //functions basically same as arozos's original function
  310. func (ldap *ldapHandler) HandleLoginPage(w http.ResponseWriter, r *http.Request) {
  311. //load the template from file and inject necessary variables
  312. red, _ := common.Mv(r, "redirect", false)
  313. //Append the redirection addr into the template
  314. imgsrc := "./web/" + ldap.iconSystem
  315. if !common.FileExists(imgsrc) {
  316. imgsrc = "./web/img/public/auth_icon.png"
  317. }
  318. imageBase64, _ := common.LoadImageAsBase64(imgsrc)
  319. parsedPage, err := common.Templateload("web/login.system", map[string]interface{}{
  320. "redirection_addr": red,
  321. "usercount": strconv.Itoa(ldap.ag.GetUserCounts()),
  322. "service_logo": imageBase64,
  323. "login_addr": "system/auth/ldap/login",
  324. })
  325. if err != nil {
  326. panic("Error. Unable to parse login page. Is web directory data exists?")
  327. }
  328. w.Header().Add("Content-Type", "text/html; charset=UTF-8")
  329. w.Write([]byte(parsedPage))
  330. }
  331. func (ldap *ldapHandler) HandleNewPasswordPage(w http.ResponseWriter, r *http.Request) {
  332. //get the parameter from the request
  333. acc, err := common.Mv(r, "username", false)
  334. if err != nil {
  335. common.SendErrorResponse(w, err.Error())
  336. return
  337. }
  338. displayname, err := common.Mv(r, "displayname", false)
  339. if err != nil {
  340. common.SendErrorResponse(w, err.Error())
  341. return
  342. }
  343. key, err := common.Mv(r, "authkey", false)
  344. if err != nil {
  345. common.SendErrorResponse(w, err.Error())
  346. return
  347. }
  348. //init the web interface
  349. imgsrc := "./web/" + ldap.iconSystem
  350. if !common.FileExists(imgsrc) {
  351. imgsrc = "./web/img/public/auth_icon.png"
  352. }
  353. imageBase64, _ := common.LoadImageAsBase64(imgsrc)
  354. template, err := common.Templateload("system/ldap/newPasswordTemplate.html", map[string]interface{}{
  355. "vendor_logo": imageBase64,
  356. "username": acc,
  357. "display_name": displayname,
  358. "key": key,
  359. })
  360. if err != nil {
  361. log.Fatal(err)
  362. }
  363. w.Header().Set("Content-Type", "text/html; charset=UTF-8")
  364. w.Write([]byte(template))
  365. }
  366. func (ldap *ldapHandler) HandleLogin(w http.ResponseWriter, r *http.Request) {
  367. //Get username from request using POST mode
  368. username, err := common.Mv(r, "username", true)
  369. if err != nil {
  370. //Username not defined
  371. log.Println("[System Auth] Someone trying to login with username: " + username)
  372. //Write to log
  373. ldap.ag.Logger.LogAuth(r, false)
  374. common.SendErrorResponse(w, "Username not defined or empty.")
  375. return
  376. }
  377. //Get password from request using POST mode
  378. password, err := common.Mv(r, "password", true)
  379. if err != nil {
  380. //Password not defined
  381. ldap.ag.Logger.LogAuth(r, false)
  382. common.SendErrorResponse(w, "Password not defined or empty.")
  383. return
  384. }
  385. //Get rememberme settings
  386. rememberme := false
  387. rmbme, _ := common.Mv(r, "rmbme", true)
  388. if rmbme == "true" {
  389. rememberme = true
  390. }
  391. //Check the database and see if this user is in the database
  392. passwordCorrect, err := ldap.ldapreader.Authenticate(username, password)
  393. if err != nil {
  394. ldap.ag.Logger.LogAuth(r, false)
  395. common.SendErrorResponse(w, "Unable to connect to LDAP server")
  396. log.Println("LDAP Authentication error, " + err.Error())
  397. return
  398. }
  399. //The database contain this user information. Check its password if it is correct
  400. if passwordCorrect {
  401. //Password correct
  402. //if user not exist then redirect to create pwd screen
  403. if !ldap.ag.UserExists(username) {
  404. authkey := ldap.syncdb.Store(username)
  405. common.SendJSONResponse(w, "{\"redirect\":\"system/auth/ldap/newPassword?username="+username+"&displayname="+username+"&authkey="+authkey+"\"}")
  406. } else {
  407. // Set user as authenticated
  408. ldap.ag.LoginUserByRequest(w, r, username, rememberme)
  409. //Print the login message to console
  410. log.Println(username + " logged in.")
  411. ldap.ag.Logger.LogAuth(r, true)
  412. common.SendOK(w)
  413. }
  414. } else {
  415. //Password incorrect
  416. log.Println(username + " has entered an invalid username or password")
  417. common.SendErrorResponse(w, "Invalid username or password")
  418. ldap.ag.Logger.LogAuth(r, false)
  419. return
  420. }
  421. }
  422. func (ldap *ldapHandler) HandleSetPassword(w http.ResponseWriter, r *http.Request) {
  423. //get paramters from request
  424. username, err := common.Mv(r, "username", true)
  425. if err != nil {
  426. common.SendErrorResponse(w, err.Error())
  427. return
  428. }
  429. password, err := common.Mv(r, "password", true)
  430. if err != nil {
  431. common.SendErrorResponse(w, err.Error())
  432. return
  433. }
  434. authkey, err := common.Mv(r, "authkey", true)
  435. if err != nil {
  436. common.SendErrorResponse(w, err.Error())
  437. return
  438. }
  439. //check if the input key matches the database's username
  440. isValid := ldap.syncdb.Read(authkey) == username
  441. ldap.syncdb.Delete(authkey) // remove the key, aka key is one time use only
  442. //if db data match the username, proceed
  443. if isValid {
  444. //if not exists
  445. if !ldap.ag.UserExists(username) {
  446. //get the user from ldap server
  447. ldapUser, err := ldap.ldapreader.GetUser(username)
  448. if err != nil {
  449. common.SendErrorResponse(w, err.Error())
  450. return
  451. }
  452. //convert the ldap usergroup to arozos usergroup
  453. convertedInfo := ldap.convertGroup(ldapUser)
  454. //create user account and login
  455. ldap.ag.CreateUserAccount(username, password, convertedInfo.EquivGroup)
  456. ldap.ag.Logger.LogAuth(r, true)
  457. ldap.ag.LoginUserByRequest(w, r, username, false)
  458. common.SendOK(w)
  459. return
  460. } else {
  461. //if exist then return error
  462. common.SendErrorResponse(w, "User exists, please contact the system administrator if you believe this is an error.")
  463. return
  464. }
  465. } else {
  466. common.SendErrorResponse(w, "Improper key detected")
  467. log.Println(r.RemoteAddr + " attempted to use invaild key to create new user but failed")
  468. return
  469. }
  470. }