ldap.go 14 KB

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