package ldap import ( "encoding/json" "log" "net/http" "regexp" "strconv" auth "imuslab.com/arozos/mod/auth" "imuslab.com/arozos/mod/auth/ldap/ldapreader" reg "imuslab.com/arozos/mod/auth/register" "imuslab.com/arozos/mod/common" db "imuslab.com/arozos/mod/database" permission "imuslab.com/arozos/mod/permission" "imuslab.com/arozos/mod/user" ) type ldapHandler struct { ag *auth.AuthAgent ldapreader *ldapreader.LdapReader reg *reg.RegisterHandler coredb *db.Database permissionHandler *permission.PermissionHandler userHandler *user.UserHandler } type Config struct { Enabled bool `json:"enabled"` AutoRedirect bool `json:"auto_redirect"` BindUsername string `json:"bind_username"` BindPassword string `json:"bind_password"` FQDN string `json:"fqdn"` BaseDN string `json:"base_dn"` } type UserAccount struct { Username string `json:"username"` Group []string `json:"group"` EquivGroup []string `json:"equiv_group"` } /* TODO: not sure why auto redirect will keep enable TODO: stop user to syncorize if the current user will lost admin access */ //NewLdapHandler xxx func NewLdapHandler(authAgent *auth.AuthAgent, register *reg.RegisterHandler, coreDb *db.Database, permissionHandler *permission.PermissionHandler, userHandler *user.UserHandler) *ldapHandler { //ldap handler init log.Println("Starting LDAP client...") err := coreDb.NewTable("ldap") if err != nil { log.Println("Failed to create LDAP database. Terminating.") panic(err) } //key value to be used for LDAP authentication BindUsername := readSingleConfig("BindUsername", coreDb) BindPassword := readSingleConfig("BindPassword", coreDb) FQDN := readSingleConfig("FQDN", coreDb) BaseDN := readSingleConfig("BaseDN", coreDb) LDAPHandler := ldapHandler{ ag: authAgent, ldapreader: ldapreader.NewLDAPReader(BindUsername, BindPassword, FQDN, BaseDN), reg: register, coredb: coreDb, permissionHandler: permissionHandler, userHandler: userHandler, } return &LDAPHandler } func (ldap *ldapHandler) ReadConfig(w http.ResponseWriter, r *http.Request) { //basic components enabled, err := strconv.ParseBool(ldap.readSingleConfig("enabled")) if err != nil { common.SendTextResponse(w, "Invalid config value [key=enabled].") return } autoredirect, err := strconv.ParseBool(ldap.readSingleConfig("autoredirect")) if err != nil { common.SendTextResponse(w, "Invalid config value [key=autoredirect].") return } //get the LDAP config from db BindUsername := ldap.readSingleConfig("BindUsername") BindPassword := ldap.readSingleConfig("BindPassword") FQDN := ldap.readSingleConfig("FQDN") BaseDN := ldap.readSingleConfig("BaseDN") //marshall it and return config, err := json.Marshal(Config{ Enabled: enabled, AutoRedirect: autoredirect, BindUsername: BindUsername, BindPassword: BindPassword, FQDN: FQDN, BaseDN: BaseDN, }) if err != nil { empty, err := json.Marshal(Config{}) if err != nil { common.SendErrorResponse(w, "Error while marshalling config") } common.SendJSONResponse(w, string(empty)) } common.SendJSONResponse(w, string(config)) } func (ldap *ldapHandler) WriteConfig(w http.ResponseWriter, r *http.Request) { enabled, err := common.Mv(r, "enabled", true) if err != nil { common.SendErrorResponse(w, "enabled field can't be empty") return } autoredirect, err := common.Mv(r, "autoredirect", true) if err != nil { common.SendErrorResponse(w, "enabled field can't be empty") return } //allow empty fields if enabled is false showError := true if enabled != "true" { showError = false } //four fields to store the LDAP authentication information BindUsername, err := common.Mv(r, "bind_username", true) if err != nil { if showError { common.SendErrorResponse(w, "bind_username field can't be empty") return } } BindPassword, err := common.Mv(r, "bind_password", true) if err != nil { if showError { common.SendErrorResponse(w, "bind_password field can't be empty") return } } FQDN, err := common.Mv(r, "fqdn", true) if err != nil { if showError { common.SendErrorResponse(w, "fqdn field can't be empty") return } } BaseDN, err := common.Mv(r, "base_dn", true) if err != nil { if showError { common.SendErrorResponse(w, "base_dn field can't be empty") return } } ldap.coredb.Write("ldap", "enabled", enabled) ldap.coredb.Write("ldap", "autoredirect", autoredirect) ldap.coredb.Write("ldap", "BindUsername", BindUsername) ldap.coredb.Write("ldap", "BindPassword", BindPassword) ldap.coredb.Write("ldap", "FQDN", FQDN) ldap.coredb.Write("ldap", "BaseDN", BaseDN) //update the new authencation infromation ldap.ldapreader = ldapreader.NewLDAPReader(BindUsername, BindPassword, FQDN, BaseDN) common.SendOK(w) } //@para limit: -1 means unlimited func (ldap *ldapHandler) getAllUser(limit int) []UserAccount { var accounts []UserAccount result, _ := ldap.ldapreader.GetAllUser() //loop through the result for i, v := range result { //check the group belongs var Group []string var EquivGroup []string regexSyntax := regexp.MustCompile("cn=([^,]+),") for _, v := range v.GetAttributeValues("memberOf") { groups := regexSyntax.FindStringSubmatch(v) if len(groups) > 0 { //check if the LDAP group is already exists in ArOZOS system if ldap.permissionHandler.GroupExists(groups[1]) { EquivGroup = append(EquivGroup, groups[1]) } //LDAP list Group = append(Group, groups[1]) } } if len(EquivGroup) < 1 { EquivGroup = append(EquivGroup, ldap.reg.DefaultUserGroup) } account := UserAccount{ Username: v.GetAttributeValue("uid"), Group: Group, EquivGroup: EquivGroup, } accounts = append(accounts, account) if i > limit && limit != -1 { break } } if len(accounts) > 0 { return accounts[1:] } else { return []UserAccount{} } } func (ldap *ldapHandler) TestConnection(w http.ResponseWriter, r *http.Request) { //marshall it and return accountJSON, err := json.Marshal(ldap.getAllUser(10)) if err != nil { empty, err := json.Marshal(UserAccount{}) if err != nil { common.SendErrorResponse(w, "Error while marshalling information") } common.SendJSONResponse(w, string(empty)) } common.SendJSONResponse(w, string(accountJSON)) } func (ldap *ldapHandler) checkCurrUserAdmin(w http.ResponseWriter, r *http.Request) bool { //check current user is admin and new update will remove it or not currentLoggedInUser, err := ldap.userHandler.GetUserInfoFromRequest(w, r) if err != nil { common.SendErrorResponse(w, "Error while getting user info") return false } ldapCurrUserInfo, err := ldap.ldapreader.GetUser(currentLoggedInUser.Username) if err != nil { common.SendErrorResponse(w, "Error while getting user info from LDAP") return false } isAdmin := false regexSyntax := regexp.MustCompile("cn=([^,]+),") for _, v := range ldapCurrUserInfo.GetAttributeValues("memberOf") { groups := regexSyntax.FindStringSubmatch(v) if len(groups) > 0 { //check if the LDAP group is already exists in ArOZOS system if ldap.permissionHandler.GroupExists(groups[1]) { if ldap.permissionHandler.GetPermissionGroupByName(groups[1]).IsAdmin { isAdmin = true } } } } return isAdmin } func (ldap *ldapHandler) SynchronizeUser(w http.ResponseWriter, r *http.Request) { consistencyCheck := ldap.checkCurrUserAdmin(w, r) if !consistencyCheck { common.SendErrorResponse(w, "You will no longer become the admin after synchronizing, synchronize terminated") return } ldapUsersList := ldap.getAllUser(-1) for _, ldapUser := range ldapUsersList { //check if user does not exist in system if !ldap.ag.UserExists(ldapUser.Username) { //TODO change password ldap.ag.CreateUserAccount(ldapUser.Username, "P@ssw0rd", ldapUser.EquivGroup) } else { //if exists, then check if the user group is the same with ldap's setting //Get the permission groups by their ids userinfo, err := ldap.userHandler.GetUserInfoFromUsername(ldapUser.Username) if err != nil { common.SendErrorResponse(w, "Error while getting user info") return } newPermissionGroups := ldap.permissionHandler.GetPermissionGroupByNameList(ldapUser.EquivGroup) //Set the user's permission to these groups userinfo.SetUserPermissionGroup(newPermissionGroups) } } common.SendOK(w) }