// Get handles GET request, it checks the http header for user credentials // and parse service and scope based on docker registry v2 standard, // checkes the permission agains local DB and generates jwt token. func (h *Handler) Get() { var uid, password, username string request := h.Ctx.Request service := h.GetString("service") scopes := h.GetStrings("scope") access := GetResourceActions(scopes) log.Infof("request url: %v", request.URL.String()) if svc_utils.VerifySecret(request) { log.Debugf("Will grant all access as this request is from job service with legal secret.") username = "******" } else { uid, password, _ = request.BasicAuth() log.Debugf("uid for logging: %s", uid) user := authenticate(uid, password) if user == nil { log.Warningf("login request with invalid credentials in token service, uid: %s", uid) if len(scopes) == 0 { h.CustomAbort(http.StatusUnauthorized, "") } } else { username = user.Username } log.Debugf("username for filtering access: %s.", username) for _, a := range access { FilterAccess(username, a) } } h.serveToken(username, service, access) }
// Start kicks off the statemachine to transit from current state to s, and moves on // It will search the transit map if the next state is "_continue", and // will enter error state if there's more than one possible path when next state is "_continue" func (sm *SM) Start(s string) { n, err := sm.EnterState(s) log.Debugf("Job id: %d, next state from handler: %s", sm.JobID, n) for len(n) > 0 && err == nil { if d := sm.getDesiredState(); len(d) > 0 { log.Debugf("Job id: %d. Desired state: %s, will ignore the next state from handler", sm.JobID, d) n = d sm.setDesiredState("") continue } if n == models.JobContinue && len(sm.Transitions[sm.CurrentState]) == 1 { for n = range sm.Transitions[sm.CurrentState] { break } log.Debugf("Job id: %d, Continue to state: %s", sm.JobID, n) continue } if n == models.JobContinue && len(sm.Transitions[sm.CurrentState]) != 1 { log.Errorf("Job id: %d, next state is continue but there are %d possible next states in transition table", sm.JobID, len(sm.Transitions[sm.CurrentState])) err = fmt.Errorf("Unable to continue") break } n, err = sm.EnterState(n) log.Debugf("Job id: %d, next state from handler: %s", sm.JobID, n) } if err != nil { log.Warningf("Job id: %d, the statemachin will enter error state due to error: %v", sm.JobID, err) sm.EnterState(models.JobError) } }
func resumeJobs() { log.Debugf("Trying to resume halted jobs...") err := dao.ResetRunningJobs() if err != nil { log.Warningf("Failed to reset all running jobs to pending, error: %v", err) } jobs, err := dao.GetRepJobByStatus(models.JobPending, models.JobRetrying) if err == nil { for _, j := range jobs { log.Debugf("Resuming job: %d", j.ID) job.Schedule(j.ID) } } else { log.Warningf("Failed to jobs to resume, error: %v", err) } }
// Prepare validates the URL and parms func (pma *ProjectMemberAPI) Prepare() { pid, err := strconv.ParseInt(pma.Ctx.Input.Param(":pid"), 10, 64) if err != nil { log.Errorf("Error parsing project id: %d, error: %v", pid, err) pma.CustomAbort(http.StatusBadRequest, "invalid project Id") return } p, err := dao.GetProjectByID(pid) if err != nil { log.Errorf("Error occurred in GetProjectById, error: %v", err) pma.CustomAbort(http.StatusInternalServerError, "Internal error.") } if p == nil { log.Warningf("Project with id: %d does not exist.", pid) pma.CustomAbort(http.StatusNotFound, "Project does not exist") } pma.project = p pma.currentUserID = pma.ValidateUser() mid := pma.Ctx.Input.Param(":mid") if mid == "current" { pma.memberID = pma.currentUserID } else if len(mid) == 0 { pma.memberID = 0 } else if len(mid) > 0 { memberID, err := strconv.Atoi(mid) if err != nil { log.Errorf("Invalid member Id, error: %v", err) pma.CustomAbort(http.StatusBadRequest, "Invalid member id") } pma.memberID = memberID } }
// Delete ... func (ua *UserAPI) Delete() { if !ua.IsAdmin { log.Warningf("current user, id: %d does not have admin role, can not remove user", ua.currentUserID) ua.RenderError(http.StatusForbidden, "User does not have admin role") return } // TODO read from conifg authMode := os.Getenv("AUTH_MODE") if authMode == "ldap_auth" { ua.CustomAbort(http.StatusForbidden, "user can not be deleted in LDAP authentication mode") } if ua.currentUserID == ua.userID { ua.CustomAbort(http.StatusForbidden, "can not delete yourself") } var err error err = dao.DeleteUser(ua.userID) if err != nil { log.Errorf("Failed to delete data from database, error: %v", err) ua.RenderError(http.StatusInternalServerError, "Failed to delete User") return } }
// FilterAccessLog handles GET to /api/projects/{}/logs func (p *ProjectAPI) FilterAccessLog() { p.userID = p.ValidateUser() var query models.AccessLog p.DecodeJSONReq(&query) if !checkProjectPermission(p.userID, p.projectID) { log.Warningf("Current user, user id: %d does not have permission to read accesslog of project, id: %d", p.userID, p.projectID) p.RenderError(http.StatusForbidden, "") return } query.ProjectID = p.projectID query.BeginTime = time.Unix(query.BeginTimestamp, 0) query.EndTime = time.Unix(query.EndTimestamp, 0) page, pageSize := p.GetPaginationParams() total, err := dao.GetTotalOfAccessLogs(query) if err != nil { log.Errorf("failed to get total of access log: %v", err) p.CustomAbort(http.StatusInternalServerError, "") } logs, err := dao.GetAccessLogs(query, pageSize, pageSize*(page-1)) if err != nil { log.Errorf("failed to get access log: %v", err) p.CustomAbort(http.StatusInternalServerError, "") } p.SetPaginationHeader(total, page, pageSize) p.Data["json"] = logs p.ServeJSON() }
// Post ... func (pma *ProjectMemberAPI) Post() { currentUserID := pma.currentUserID projectID := pma.project.ProjectID if !hasProjectAdminRole(currentUserID, projectID) { log.Warningf("Current user, id: %d does not have project admin role for project, id:", currentUserID, projectID) pma.RenderError(http.StatusForbidden, "") return } var req memberReq pma.DecodeJSONReq(&req) username := req.Username userID := checkUserExists(username) if userID <= 0 { log.Warningf("User does not exist, user name: %s", username) pma.RenderError(http.StatusNotFound, "User does not exist") return } rolelist, err := dao.GetUserProjectRoles(userID, projectID) if err != nil { log.Errorf("Error occurred in GetUserProjectRoles, error: %v", err) pma.CustomAbort(http.StatusInternalServerError, "Internal error.") } if len(rolelist) > 0 { log.Warningf("user is already added to project, user id: %d, project id: %d", userID, projectID) pma.RenderError(http.StatusConflict, "user is ready in project") return } if len(req.Roles) <= 0 || len(req.Roles) > 1 { pma.CustomAbort(http.StatusBadRequest, "only one role is supported") } rid := req.Roles[0] if !(rid == models.PROJECTADMIN || rid == models.DEVELOPER || rid == models.GUEST) { pma.CustomAbort(http.StatusBadRequest, "invalid role") } err = dao.AddProjectMember(projectID, userID, rid) if err != nil { log.Errorf("Failed to update DB to add project user role, project id: %d, user id: %d, role id: %d", projectID, userID, rid) pma.RenderError(http.StatusInternalServerError, "Failed to update data in database") return } }
// VerifySecret verifies the UI_SECRET cookie in a http request. func VerifySecret(r *http.Request) bool { secret := os.Getenv("UI_SECRET") c, err := r.Cookie("uisecret") if err != nil { log.Warningf("Failed to get secret cookie, error: %v", err) } return c != nil && c.Value == secret }
// VerifySecret verifies the UI_SECRET cookie in a http request. func VerifySecret(r *http.Request) bool { secret := config.UISecret() c, err := r.Cookie("uisecret") if err != nil { log.Warningf("Failed to get secret cookie, error: %v", err) } return c != nil && c.Value == secret }
// Enter updates the status of a job and returns "_continue" status to tell state machine to move on. // If the status is a final status it returns empty string and the state machine will be stopped. func (su StatusUpdater) Enter() (string, error) { err := dao.UpdateRepJobStatus(su.JobID, su.State) if err != nil { log.Warningf("Failed to update state of job: %d, state: %s, error: %v", su.JobID, su.State, err) } var next = models.JobContinue if su.State == models.JobStopped || su.State == models.JobError || su.State == models.JobFinished { next = "" } return next, err }
// Parse parses the auth settings url settings and other configuration consumed by code under src/ui func (up *uiParser) Parse(raw map[string]string, config map[string]interface{}) error { mode := raw["AUTH_MODE"] if mode == "ldap_auth" { setting := LDAPSetting{ URL: raw["LDAP_URL"], BaseDn: raw["LDAP_BASE_DN"], SearchDn: raw["LDAP_SEARCH_DN"], SearchPwd: raw["LDAP_SEARCH_PWD"], UID: raw["LDAP_UID"], Filter: raw["LDAP_FILTER"], Scope: raw["LDAP_SCOPE"], } config["ldap"] = setting } config["auth_mode"] = mode var tokenExpiration = 30 //minutes if len(raw["TOKEN_EXPIRATION"]) > 0 { i, err := strconv.Atoi(raw["TOKEN_EXPIRATION"]) if err != nil { log.Warningf("failed to parse token expiration: %v, using default value %d", err, tokenExpiration) } else if i <= 0 { log.Warningf("invalid token expiration, using default value: %d minutes", tokenExpiration) } else { tokenExpiration = i } } config["token_exp"] = tokenExpiration config["admin_password"] = raw["HARBOR_ADMIN_PASSWORD"] config["ext_reg_url"] = raw["EXT_REG_URL"] config["ui_secret"] = raw["UI_SECRET"] config["secret_key"] = raw["SECRET_KEY"] config["self_registration"] = raw["SELF_REGISTRATION"] != "off" config["admin_create_project"] = strings.ToLower(raw["PROJECT_CREATION_RESTRICTION"]) == "adminonly" registryURL := raw["REGISTRY_URL"] registryURL = strings.TrimRight(registryURL, "/") config["internal_registry_url"] = registryURL jobserviceURL := raw["JOB_SERVICE_URL"] jobserviceURL = strings.TrimRight(jobserviceURL, "/") config["internal_jobservice_url"] = jobserviceURL return nil }
// ToggleUserAdminRole handles PUT api/users/{}/sysadmin func (ua *UserAPI) ToggleUserAdminRole() { if !ua.IsAdmin { log.Warningf("current user, id: %d does not have admin role, can not update other user's role", ua.currentUserID) ua.RenderError(http.StatusForbidden, "User does not have admin role") return } userQuery := models.User{UserID: ua.userID} ua.DecodeJSONReq(&userQuery) if err := dao.ToggleUserAdminRole(userQuery.UserID, userQuery.HasAdminRole); err != nil { log.Errorf("Error occurred in ToggleUserAdminRole: %v", err) ua.CustomAbort(http.StatusInternalServerError, "Internal error.") } }
// Get renders project page func (pc *ProjectController) Get() { var err error isSysAdmin := false uid := pc.GetSession("userId") if uid != nil { isSysAdmin, err = dao.IsAdminRole(uid) if err != nil { log.Warningf("Error in checking Admin Role for user, id: %d, error: %v", uid, err) isSysAdmin = false } } pc.Data["CanCreate"] = !config.OnlyAdminCreateProject() || isSysAdmin pc.Forward("page_title_project", "project.htm") }
// Put ... func (pma *ProjectMemberAPI) Put() { currentUserID := pma.currentUserID pid := pma.project.ProjectID if !hasProjectAdminRole(currentUserID, pid) { log.Warningf("Current user, id: %d does not have project admin role for project, id:", currentUserID, pid) pma.RenderError(http.StatusForbidden, "") return } mid := pma.memberID var req memberReq pma.DecodeJSONReq(&req) roleList, err := dao.GetUserProjectRoles(mid, pid) if len(roleList) == 0 { log.Warningf("User is not in project, user id: %d, project id: %d", mid, pid) pma.RenderError(http.StatusNotFound, "user not exist in project") return } //TODO: delete and insert should in one transaction //delete user project role record for the given user err = dao.DeleteProjectMember(pid, mid) if err != nil { log.Errorf("Failed to delete project roles for user, user id: %d, project id: %d, error: %v", mid, pid, err) pma.RenderError(http.StatusInternalServerError, "Failed to update data in DB") return } //insert roles in request for _, rid := range req.Roles { err = dao.AddProjectMember(pid, mid, int(rid)) if err != nil { log.Errorf("Failed to update DB to add project user role, project id: %d, user id: %d, role id: %d", pid, mid, rid) pma.RenderError(http.StatusInternalServerError, "Failed to update data in database") return } } }
// Get renders optional menu, Admin user has "Add User" menu func (omc *OptionalMenuController) Get() { sessionUserID := omc.GetSession("userId") var hasLoggedIn bool var allowAddNew bool var isAdminForLdap bool var allowSettingAccount bool if sessionUserID != nil { hasLoggedIn = true userID := sessionUserID.(int) u, err := dao.GetUser(models.User{UserID: userID}) if err != nil { log.Errorf("Error occurred in GetUser, error: %v", err) omc.CustomAbort(http.StatusInternalServerError, "Internal error.") } if u == nil { log.Warningf("User was deleted already, user id: %d, canceling request.", userID) omc.CustomAbort(http.StatusUnauthorized, "") } omc.Data["Username"] = u.Username if userID == 1 { isAdminForLdap = true } if omc.AuthMode == "db_auth" || isAdminForLdap { allowSettingAccount = true } isAdmin, err := dao.IsAdminRole(sessionUserID.(int)) if err != nil { log.Errorf("Error occurred in IsAdminRole: %v", err) omc.CustomAbort(http.StatusInternalServerError, "") } if isAdmin && omc.AuthMode == "db_auth" { allowAddNew = true } } omc.Data["AddNew"] = allowAddNew omc.Data["SettingAccount"] = allowSettingAccount omc.Data["HasLoggedIn"] = hasLoggedIn omc.TplName = "optional-menu.htm" omc.Render() }
// Put ... func (ua *UserAPI) Put() { ldapAdminUser := (ua.AuthMode == "ldap_auth" && ua.userID == 1 && ua.userID == ua.currentUserID) if !(ua.AuthMode == "db_auth" || ldapAdminUser) { ua.CustomAbort(http.StatusForbidden, "") } if !ua.IsAdmin { if ua.userID != ua.currentUserID { log.Warning("Guests can only change their own account.") ua.CustomAbort(http.StatusForbidden, "Guests can only change their own account.") } } user := models.User{UserID: ua.userID} ua.DecodeJSONReq(&user) err := commonValidate(user) if err != nil { log.Warningf("Bad request in change user profile: %v", err) ua.RenderError(http.StatusBadRequest, "change user profile error:"+err.Error()) return } userQuery := models.User{UserID: ua.userID} u, err := dao.GetUser(userQuery) if err != nil { log.Errorf("Error occurred in GetUser, error: %v", err) ua.CustomAbort(http.StatusInternalServerError, "Internal error.") } if u == nil { log.Errorf("User with Id: %d does not exist", ua.userID) ua.CustomAbort(http.StatusNotFound, "") } if u.Email != user.Email { emailExist, err := dao.UserExists(user, "email") if err != nil { log.Errorf("Error occurred in change user profile: %v", err) ua.CustomAbort(http.StatusInternalServerError, "Internal error.") } if emailExist { log.Warning("email has already been used!") ua.RenderError(http.StatusConflict, "email has already been used!") return } } if err := dao.ChangeUserProfile(user); err != nil { log.Errorf("Failed to update user profile, error: %v", err) ua.CustomAbort(http.StatusInternalServerError, err.Error()) } }
// Post ... func (ua *UserAPI) Post() { if !(ua.AuthMode == "db_auth") { ua.CustomAbort(http.StatusForbidden, "") } if !(ua.SelfRegistration || ua.IsAdmin) { log.Warning("Registration can only be used by admin role user when self-registration is off.") ua.CustomAbort(http.StatusForbidden, "") } user := models.User{} ua.DecodeJSONReq(&user) err := validate(user) if err != nil { log.Warningf("Bad request in Register: %v", err) ua.RenderError(http.StatusBadRequest, "register error:"+err.Error()) return } userExist, err := dao.UserExists(user, "username") if err != nil { log.Errorf("Error occurred in Register: %v", err) ua.CustomAbort(http.StatusInternalServerError, "Internal error.") } if userExist { log.Warning("username has already been used!") ua.RenderError(http.StatusConflict, "username has already been used!") return } emailExist, err := dao.UserExists(user, "email") if err != nil { log.Errorf("Error occurred in change user profile: %v", err) ua.CustomAbort(http.StatusInternalServerError, "Internal error.") } if emailExist { log.Warning("email has already been used!") ua.RenderError(http.StatusConflict, "email has already been used!") return } userID, err := dao.Register(user) if err != nil { log.Errorf("Error occurred in Register: %v", err) ua.CustomAbort(http.StatusInternalServerError, "Internal error.") } ua.Redirect(http.StatusCreated, strconv.FormatInt(userID, 10)) }
// Delete ... func (pma *ProjectMemberAPI) Delete() { currentUserID := pma.currentUserID pid := pma.project.ProjectID if !hasProjectAdminRole(currentUserID, pid) { log.Warningf("Current user, id: %d does not have project admin role for project, id:", currentUserID, pid) pma.RenderError(http.StatusForbidden, "") return } mid := pma.memberID err := dao.DeleteProjectMember(pid, mid) if err != nil { log.Errorf("Failed to delete project roles for user, user id: %d, project id: %d, error: %v", mid, pid, err) pma.RenderError(http.StatusInternalServerError, "Failed to update data in DB") return } }
// ValidateUser checks if the request triggered by a valid user func (b *BaseAPI) ValidateUser() int { userID, needsCheck, ok := b.GetUserIDForRequest() if !ok { log.Warning("No user id in session, canceling request") b.CustomAbort(http.StatusUnauthorized, "") } if needsCheck { u, err := dao.GetUser(models.User{UserID: userID}) if err != nil { log.Errorf("Error occurred in GetUser, error: %v", err) b.CustomAbort(http.StatusInternalServerError, "Internal error.") } if u == nil { log.Warningf("User was deleted already, user id: %d, canceling request.", userID) b.CustomAbort(http.StatusUnauthorized, "") } } return userID }
func init() { // TODO read it from config expi := os.Getenv("TOKEN_EXPIRATION") if len(expi) != 0 { i, err := strconv.Atoi(expi) if err != nil { log.Errorf("failed to parse token expiration: %v, using default value: %d minutes", err, expiration) return } if i <= 0 { log.Warningf("invalid token expiration, using default value: %d minutes", expiration) return } expiration = i } log.Infof("token expiration: %d minutes", expiration) }
// Get ... func (pma *ProjectMemberAPI) Get() { pid := pma.project.ProjectID if !checkProjectPermission(pma.currentUserID, pid) { log.Warningf("Current user, user id: %d does not have permission for project, id: %d", pma.currentUserID, pid) pma.RenderError(http.StatusForbidden, "") return } if pma.memberID == 0 { //member id not set return list of the members username := pma.GetString("username") queryUser := models.User{Username: username} userList, err := dao.GetUserByProject(pid, queryUser) if err != nil { log.Errorf("Failed to query database for member list, error: %v", err) pma.RenderError(http.StatusInternalServerError, "Internal Server Error") return } pma.Data["json"] = userList } else { //return detail of a member roleList, err := listRoles(pma.memberID, pid) if err != nil { log.Errorf("Error occurred in GetUserProjectRoles, error: %v", err) pma.CustomAbort(http.StatusInternalServerError, "Internal error.") } if len(roleList) == 0 { pma.CustomAbort(http.StatusNotFound, fmt.Sprintf("user %d is not a member of the project", pma.memberID)) } //return empty role list to indicate if a user is not a member result := make(map[string]interface{}) user, err := dao.GetUser(models.User{UserID: pma.memberID}) if err != nil { log.Errorf("Error occurred in GetUser, error: %v", err) pma.CustomAbort(http.StatusInternalServerError, "Internal error.") } result["username"] = user.Username result["user_id"] = pma.memberID result["roles"] = roleList pma.Data["json"] = result } pma.ServeJSON() }
// Get renders user's navigation details header func (ndc *NavigationDetailController) Get() { sessionUserID := ndc.GetSession("userId") var isAdmin int if sessionUserID != nil { userID := sessionUserID.(int) u, err := dao.GetUser(models.User{UserID: userID}) if err != nil { log.Errorf("Error occurred in GetUser, error: %v", err) ndc.CustomAbort(http.StatusInternalServerError, "Internal error.") } if u == nil { log.Warningf("User was deleted already, user id: %d, canceling request.", userID) ndc.CustomAbort(http.StatusUnauthorized, "") } isAdmin = u.HasAdminRole } ndc.Data["IsAdmin"] = isAdmin ndc.TplName = "navigation-detail.htm" ndc.Render() }
//Get renders sign_in page func (sic *SignInController) Get() { sessionUserID := sic.GetSession("userId") var hasLoggedIn bool var username string if sessionUserID != nil { hasLoggedIn = true userID := sessionUserID.(int) u, err := dao.GetUser(models.User{UserID: userID}) if err != nil { log.Errorf("Error occurred in GetUser, error: %v", err) sic.CustomAbort(http.StatusInternalServerError, "Internal error.") } if u == nil { log.Warningf("User was deleted already, user id: %d, canceling request.", userID) sic.CustomAbort(http.StatusUnauthorized, "") } username = u.Username } sic.Data["AuthMode"] = sic.AuthMode sic.Data["Username"] = username sic.Data["HasLoggedIn"] = hasLoggedIn sic.TplName = "sign-in.htm" sic.Render() }
// ToggleProjectPublic ... func (p *ProjectAPI) ToggleProjectPublic() { p.userID = p.ValidateUser() var req projectReq projectID, err := strconv.ParseInt(p.Ctx.Input.Param(":id"), 10, 64) if err != nil { log.Errorf("Error parsing project id: %d, error: %v", projectID, err) p.RenderError(http.StatusBadRequest, "invalid project id") return } p.DecodeJSONReq(&req) public := req.Public if !isProjectAdmin(p.userID, projectID) { log.Warningf("Current user, id: %d does not have project admin role for project, id: %d", p.userID, projectID) p.RenderError(http.StatusForbidden, "") return } err = dao.ToggleProjectPublicity(p.projectID, public) if err != nil { log.Errorf("Error while updating project, project id: %d, error: %v", projectID, err) p.RenderError(http.StatusInternalServerError, "Failed to update project") } }
func init() { maxWorkersEnv := os.Getenv("MAX_JOB_WORKERS") maxWorkers64, err := strconv.ParseInt(maxWorkersEnv, 10, 32) maxJobWorkers = int(maxWorkers64) if err != nil { log.Warningf("Failed to parse max works setting, error: %v, the default value: %d will be used", err, defaultMaxWorkers) maxJobWorkers = defaultMaxWorkers } localRegURL = os.Getenv("REGISTRY_URL") if len(localRegURL) == 0 { localRegURL = "http://registry:5000" } localUIURL = os.Getenv("UI_URL") if len(localUIURL) == 0 { localUIURL = "http://ui" } logDir = os.Getenv("LOG_DIR") if len(logDir) == 0 { logDir = "/var/log" } f, err := os.Open(logDir) defer f.Close() if err != nil { panic(err) } finfo, err := f.Stat() if err != nil { panic(err) } if !finfo.IsDir() { panic(fmt.Sprintf("%s is not a direcotry", logDir)) } uiSecret = os.Getenv("UI_SECRET") if len(uiSecret) == 0 { panic("UI Secret is not set") } verifyRemoteCert = os.Getenv("VERIFY_REMOTE_CERT") if len(verifyRemoteCert) == 0 { verifyRemoteCert = "on" } configPath := os.Getenv("CONFIG_PATH") if len(configPath) != 0 { log.Infof("Config path: %s", configPath) beego.LoadAppConfig("ini", configPath) } secretKey = os.Getenv("SECRET_KEY") if len(secretKey) != 16 { panic("The length of secretkey has to be 16 characters!") } log.Debugf("config: maxJobWorkers: %d", maxJobWorkers) log.Debugf("config: localUIURL: %s", localUIURL) log.Debugf("config: localRegURL: %s", localRegURL) log.Debugf("config: verifyRemoteCert: %s", verifyRemoteCert) log.Debugf("config: logDir: %s", logDir) log.Debugf("config: uiSecret: ******") }
// Authenticate checks user's credential against LDAP based on basedn template and LDAP URL, // if the check is successful a dummy record will be inserted into DB, such that this user can // be associated to other entities in the system. func (l *Auth) Authenticate(m models.AuthModel) (*models.User, error) { p := m.Principal for _, c := range metaChars { if strings.ContainsRune(p, c) { return nil, fmt.Errorf("the principal contains meta char: %q", c) } } ldapURL := config.LDAP().URL if ldapURL == "" { return nil, errors.New("can not get any available LDAP_URL") } log.Debug("ldapURL:", ldapURL) ldap, err := openldap.Initialize(ldapURL) if err != nil { return nil, err } ldap.SetOption(openldap.LDAP_OPT_PROTOCOL_VERSION, openldap.LDAP_VERSION3) ldapBaseDn := config.LDAP().BaseDn if ldapBaseDn == "" { return nil, errors.New("can not get any available LDAP_BASE_DN") } log.Debug("baseDn:", ldapBaseDn) ldapSearchDn := config.LDAP().SearchDn if ldapSearchDn != "" { log.Debug("Search DN: ", ldapSearchDn) ldapSearchPwd := config.LDAP().SearchPwd err = ldap.Bind(ldapSearchDn, ldapSearchPwd) if err != nil { log.Debug("Bind search dn error", err) return nil, err } } attrName := config.LDAP().UID filter := config.LDAP().Filter if filter != "" { filter = "(&" + filter + "(" + attrName + "=" + m.Principal + "))" } else { filter = "(" + attrName + "=" + m.Principal + ")" } log.Debug("one or more filter", filter) ldapScope := config.LDAP().Scope var scope int if ldapScope == "1" { scope = openldap.LDAP_SCOPE_BASE } else if ldapScope == "2" { scope = openldap.LDAP_SCOPE_ONELEVEL } else { scope = openldap.LDAP_SCOPE_SUBTREE } attributes := []string{"uid", "cn", "mail", "email"} result, err := ldap.SearchAll(ldapBaseDn, scope, filter, attributes) if err != nil { return nil, err } if len(result.Entries()) == 0 { log.Warningf("Not found an entry.") return nil, nil } else if len(result.Entries()) != 1 { log.Warningf("Found more than one entry.") return nil, nil } en := result.Entries()[0] bindDN := en.Dn() log.Debug("found entry:", en) err = ldap.Bind(bindDN, m.Password) if err != nil { log.Debug("Bind user error", err) return nil, err } defer ldap.Close() u := models.User{} for _, attr := range en.Attributes() { val := attr.Values()[0] switch attr.Name() { case "uid": u.Realname = val case "cn": u.Realname = val case "mail": u.Email = val case "email": u.Email = val } } u.Username = m.Principal log.Debug("username:"******",email:", u.Email) exist, err := dao.UserExists(u, "username") if err != nil { return nil, err } if exist { currentUser, err := dao.GetUser(u) if err != nil { return nil, err } u.UserID = currentUser.UserID } else { u.Realname = m.Principal u.Password = "******" u.Comment = "registered from LDAP." if u.Email == "" { u.Email = u.Username + "@placeholder.com" } userID, err := dao.Register(u) if err != nil { return nil, err } u.UserID = int(userID) } return &u, nil }