ldap.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  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. reg "imuslab.com/arozos/mod/auth/register"
  11. "imuslab.com/arozos/mod/common"
  12. db "imuslab.com/arozos/mod/database"
  13. permission "imuslab.com/arozos/mod/permission"
  14. "imuslab.com/arozos/mod/user"
  15. )
  16. type ldapHandler struct {
  17. ag *auth.AuthAgent
  18. ldapreader *ldapreader.LdapReader
  19. reg *reg.RegisterHandler
  20. coredb *db.Database
  21. permissionHandler *permission.PermissionHandler
  22. userHandler *user.UserHandler
  23. iconSystem string
  24. }
  25. type Config struct {
  26. Enabled bool `json:"enabled"`
  27. AutoRedirect bool `json:"auto_redirect"`
  28. BindUsername string `json:"bind_username"`
  29. BindPassword string `json:"bind_password"`
  30. FQDN string `json:"fqdn"`
  31. BaseDN string `json:"base_dn"`
  32. }
  33. type UserAccount struct {
  34. Username string `json:"username"`
  35. Group []string `json:"group"`
  36. EquivGroup []string `json:"equiv_group"`
  37. }
  38. /*
  39. TODO: not sure why auto redirect will keep enable
  40. TODO: stop user to syncorize if the current user will lost admin access
  41. */
  42. //NewLdapHandler xxx
  43. func NewLdapHandler(authAgent *auth.AuthAgent, register *reg.RegisterHandler, coreDb *db.Database, permissionHandler *permission.PermissionHandler, userHandler *user.UserHandler, iconSystem string) *ldapHandler {
  44. //ldap handler init
  45. log.Println("Starting LDAP client...")
  46. err := coreDb.NewTable("ldap")
  47. if err != nil {
  48. log.Println("Failed to create LDAP database. Terminating.")
  49. panic(err)
  50. }
  51. //key value to be used for LDAP authentication
  52. BindUsername := readSingleConfig("BindUsername", coreDb)
  53. BindPassword := readSingleConfig("BindPassword", coreDb)
  54. FQDN := readSingleConfig("FQDN", coreDb)
  55. BaseDN := readSingleConfig("BaseDN", coreDb)
  56. LDAPHandler := ldapHandler{
  57. ag: authAgent,
  58. ldapreader: ldapreader.NewLDAPReader(BindUsername, BindPassword, FQDN, BaseDN),
  59. reg: register,
  60. coredb: coreDb,
  61. permissionHandler: permissionHandler,
  62. userHandler: userHandler,
  63. iconSystem: iconSystem,
  64. }
  65. return &LDAPHandler
  66. }
  67. func (ldap *ldapHandler) ReadConfig(w http.ResponseWriter, r *http.Request) {
  68. //basic components
  69. enabled, err := strconv.ParseBool(ldap.readSingleConfig("enabled"))
  70. if err != nil {
  71. common.SendTextResponse(w, "Invalid config value [key=enabled].")
  72. return
  73. }
  74. autoredirect, err := strconv.ParseBool(ldap.readSingleConfig("autoredirect"))
  75. if err != nil {
  76. common.SendTextResponse(w, "Invalid config value [key=autoredirect].")
  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. AutoRedirect: autoredirect,
  88. BindUsername: BindUsername,
  89. BindPassword: BindPassword,
  90. FQDN: FQDN,
  91. BaseDN: BaseDN,
  92. })
  93. if err != nil {
  94. empty, err := json.Marshal(Config{})
  95. if err != nil {
  96. common.SendErrorResponse(w, "Error while marshalling config")
  97. }
  98. common.SendJSONResponse(w, string(empty))
  99. }
  100. common.SendJSONResponse(w, string(config))
  101. }
  102. func (ldap *ldapHandler) WriteConfig(w http.ResponseWriter, r *http.Request) {
  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. autoredirect, err := common.Mv(r, "autoredirect", true)
  109. if err != nil {
  110. common.SendErrorResponse(w, "enabled field can't be empty")
  111. return
  112. }
  113. //allow empty fields if enabled is false
  114. showError := true
  115. if enabled != "true" {
  116. showError = false
  117. }
  118. //four fields to store the LDAP authentication information
  119. BindUsername, err := common.Mv(r, "bind_username", true)
  120. if err != nil {
  121. if showError {
  122. common.SendErrorResponse(w, "bind_username field can't be empty")
  123. return
  124. }
  125. }
  126. BindPassword, err := common.Mv(r, "bind_password", true)
  127. if err != nil {
  128. if showError {
  129. common.SendErrorResponse(w, "bind_password field can't be empty")
  130. return
  131. }
  132. }
  133. FQDN, err := common.Mv(r, "fqdn", true)
  134. if err != nil {
  135. if showError {
  136. common.SendErrorResponse(w, "fqdn field can't be empty")
  137. return
  138. }
  139. }
  140. BaseDN, err := common.Mv(r, "base_dn", true)
  141. if err != nil {
  142. if showError {
  143. common.SendErrorResponse(w, "base_dn field can't be empty")
  144. return
  145. }
  146. }
  147. ldap.coredb.Write("ldap", "enabled", enabled)
  148. ldap.coredb.Write("ldap", "autoredirect", autoredirect)
  149. ldap.coredb.Write("ldap", "BindUsername", BindUsername)
  150. ldap.coredb.Write("ldap", "BindPassword", BindPassword)
  151. ldap.coredb.Write("ldap", "FQDN", FQDN)
  152. ldap.coredb.Write("ldap", "BaseDN", BaseDN)
  153. //update the new authencation infromation
  154. ldap.ldapreader = ldapreader.NewLDAPReader(BindUsername, BindPassword, FQDN, BaseDN)
  155. common.SendOK(w)
  156. }
  157. //@para limit: -1 means unlimited
  158. func (ldap *ldapHandler) getAllUser(limit int) []UserAccount {
  159. var accounts []UserAccount
  160. result, _ := ldap.ldapreader.GetAllUser()
  161. //loop through the result
  162. for i, v := range result {
  163. //check the group belongs
  164. var Group []string
  165. var EquivGroup []string
  166. regexSyntax := regexp.MustCompile("cn=([^,]+),")
  167. for _, v := range v.GetAttributeValues("memberOf") {
  168. groups := regexSyntax.FindStringSubmatch(v)
  169. if len(groups) > 0 {
  170. //check if the LDAP group is already exists in ArOZOS system
  171. if ldap.permissionHandler.GroupExists(groups[1]) {
  172. EquivGroup = append(EquivGroup, groups[1])
  173. }
  174. //LDAP list
  175. Group = append(Group, groups[1])
  176. }
  177. }
  178. if len(EquivGroup) < 1 {
  179. EquivGroup = append(EquivGroup, ldap.reg.DefaultUserGroup)
  180. }
  181. account := UserAccount{
  182. Username: v.GetAttributeValue("cn"),
  183. Group: Group,
  184. EquivGroup: EquivGroup,
  185. }
  186. accounts = append(accounts, account)
  187. if i > limit && limit != -1 {
  188. break
  189. }
  190. }
  191. if len(accounts) > 0 {
  192. return accounts[1:]
  193. } else {
  194. return []UserAccount{}
  195. }
  196. }
  197. func (ldap *ldapHandler) TestConnection(w http.ResponseWriter, r *http.Request) {
  198. //marshall it and return
  199. accountJSON, err := json.Marshal(ldap.getAllUser(10))
  200. if err != nil {
  201. empty, err := json.Marshal(UserAccount{})
  202. if err != nil {
  203. common.SendErrorResponse(w, "Error while marshalling information")
  204. }
  205. common.SendJSONResponse(w, string(empty))
  206. }
  207. common.SendJSONResponse(w, string(accountJSON))
  208. }
  209. func (ldap *ldapHandler) checkCurrUserAdmin(w http.ResponseWriter, r *http.Request) bool {
  210. //check current user is admin and new update will remove it or not
  211. currentLoggedInUser, err := ldap.userHandler.GetUserInfoFromRequest(w, r)
  212. if err != nil {
  213. common.SendErrorResponse(w, "Error while getting user info")
  214. return false
  215. }
  216. ldapCurrUserInfo, err := ldap.ldapreader.GetUser(currentLoggedInUser.Username)
  217. if err != nil {
  218. common.SendErrorResponse(w, "Error while getting user info from LDAP")
  219. return false
  220. }
  221. isAdmin := false
  222. regexSyntax := regexp.MustCompile("cn=([^,]+),")
  223. for _, v := range ldapCurrUserInfo.GetAttributeValues("memberOf") {
  224. groups := regexSyntax.FindStringSubmatch(v)
  225. if len(groups) > 0 {
  226. //check if the LDAP group is already exists in ArOZOS system
  227. if ldap.permissionHandler.GroupExists(groups[1]) {
  228. if ldap.permissionHandler.GetPermissionGroupByName(groups[1]).IsAdmin {
  229. isAdmin = true
  230. }
  231. }
  232. }
  233. }
  234. return isAdmin
  235. }
  236. func (ldap *ldapHandler) SynchronizeUser(w http.ResponseWriter, r *http.Request) {
  237. consistencyCheck := ldap.checkCurrUserAdmin(w, r)
  238. if !consistencyCheck {
  239. common.SendErrorResponse(w, "You will no longer become the admin after synchronizing, synchronize terminated")
  240. return
  241. }
  242. ldapUsersList := ldap.getAllUser(-1)
  243. for _, ldapUser := range ldapUsersList {
  244. //check if user does not exist in system
  245. if !ldap.ag.UserExists(ldapUser.Username) {
  246. //TODO change password
  247. ldap.ag.CreateUserAccount(ldapUser.Username, "P@ssw0rd", ldapUser.EquivGroup)
  248. } else {
  249. //if exists, then check if the user group is the same with ldap's setting
  250. //Get the permission groups by their ids
  251. userinfo, err := ldap.userHandler.GetUserInfoFromUsername(ldapUser.Username)
  252. if err != nil {
  253. common.SendErrorResponse(w, "Error while getting user info")
  254. return
  255. }
  256. newPermissionGroups := ldap.permissionHandler.GetPermissionGroupByNameList(ldapUser.EquivGroup)
  257. //Set the user's permission to these groups
  258. userinfo.SetUserPermissionGroup(newPermissionGroups)
  259. }
  260. }
  261. common.SendOK(w)
  262. }
  263. //LOGIN related command
  264. func (ldap *ldapHandler) HandleLoginPage(w http.ResponseWriter, r *http.Request) {
  265. red, _ := common.Mv(r, "redirect", false)
  266. //Append the redirection addr into the template
  267. imgsrc := "./web/" + ldap.iconSystem
  268. if !common.FileExists(imgsrc) {
  269. imgsrc = "./web/img/public/auth_icon.png"
  270. }
  271. imageBase64, _ := common.LoadImageAsBase64(imgsrc)
  272. parsedPage, err := common.Templateload("web/login.system", map[string]interface{}{
  273. "redirection_addr": red,
  274. "usercount": strconv.Itoa(ldap.ag.GetUserCounts()),
  275. "service_logo": imageBase64,
  276. "login_addr": "system/ldap/auth/login",
  277. })
  278. if err != nil {
  279. panic("Error. Unable to parse login page. Is web directory data exists?")
  280. }
  281. w.Header().Add("Content-Type", "text/html; charset=UTF-8")
  282. w.Write([]byte(parsedPage))
  283. }
  284. func (ldap *ldapHandler) HandleLogin(w http.ResponseWriter, r *http.Request) {
  285. username, err := common.Mv(r, "username", true)
  286. if err != nil {
  287. common.SendErrorResponse(w, err.Error())
  288. return
  289. }
  290. password, err := common.Mv(r, "password", true)
  291. if err != nil {
  292. common.SendErrorResponse(w, err.Error())
  293. return
  294. }
  295. loginInfo, err := ldap.ldapreader.Authenticate(username, password)
  296. //authencate successful
  297. //common
  298. }