ldap.go 15 KB

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