ldap.go 13 KB

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