// FilterAccess modify the action list in access based on permission func FilterAccess(username string, a *token.ResourceActions) { if a.Type == "registry" && a.Name == "catalog" { log.Infof("current access, type: %s, name:%s, actions:%v \n", a.Type, a.Name, a.Actions) return } //clear action list to assign to new acess element after perm check. a.Actions = []string{} if a.Type == "repository" { repoSplit := strings.Split(a.Name, "/") repoLength := len(repoSplit) if repoLength > 1 { //Only check the permission when the requested image has a namespace, i.e. project var projectName string registryURL := config.ExtRegistryURL() if repoSplit[0] == registryURL { projectName = repoSplit[1] log.Infof("Detected Registry URL in Project Name. Assuming this is a notary request and setting Project Name as %s\n", projectName) } else { projectName = repoSplit[0] } var permission string if len(username) > 0 { isAdmin, err := dao.IsAdminRole(username) if err != nil { log.Errorf("Error occurred in IsAdminRole: %v", err) } if isAdmin { exist, err := dao.ProjectExists(projectName) if err != nil { log.Errorf("Error occurred in CheckExistProject: %v", err) return } if exist { permission = "RWM" } else { permission = "" log.Infof("project %s does not exist, set empty permission for admin\n", projectName) } } else { permission, err = dao.GetPermission(username, projectName) if err != nil { log.Errorf("Error occurred in GetPermission: %v", err) return } } } if strings.Contains(permission, "W") { a.Actions = append(a.Actions, "push") } if strings.Contains(permission, "M") { a.Actions = append(a.Actions, "*") } if strings.Contains(permission, "R") || dao.IsProjectPublic(projectName) { a.Actions = append(a.Actions, "pull") } } } log.Infof("current access, type: %s, name:%s, actions:%v \n", a.Type, a.Name, a.Actions) }
func updateInitPassword(userID int, password string) error { queryUser := models.User{UserID: userID} user, err := dao.GetUser(queryUser) if err != nil { return fmt.Errorf("Failed to get user, userID: %d %v", userID, err) } if user == nil { return fmt.Errorf("user id: %d does not exist", userID) } if user.Salt == "" { salt := utils.GenerateRandomString() user.Salt = salt user.Password = password err = dao.ChangeUserPassword(*user) if err != nil { return fmt.Errorf("Failed to update user encrypted password, userID: %d, err: %v", userID, err) } log.Infof("User id: %d updated its encypted password successfully.", userID) } else { log.Infof("User id: %d already has its encrypted password.", userID) } return nil }
// FilterAccess modify the action list in access based on permission func FilterAccess(username string, a *token.ResourceActions) { if a.Type == "registry" && a.Name == "catalog" { log.Infof("current access, type: %s, name:%s, actions:%v \n", a.Type, a.Name, a.Actions) return } //clear action list to assign to new acess element after perm check. a.Actions = []string{} if a.Type == "repository" { if strings.Contains(a.Name, "/") { //Only check the permission when the requested image has a namespace, i.e. project projectName := a.Name[0:strings.LastIndex(a.Name, "/")] var permission string if len(username) > 0 { isAdmin, err := dao.IsAdminRole(username) if err != nil { log.Errorf("Error occurred in IsAdminRole: %v", err) } if isAdmin { exist, err := dao.ProjectExists(projectName) if err != nil { log.Errorf("Error occurred in CheckExistProject: %v", err) return } if exist { permission = "RWM" } else { permission = "" log.Infof("project %s does not exist, set empty permission for admin\n", projectName) } } else { permission, err = dao.GetPermission(username, projectName) if err != nil { log.Errorf("Error occurred in GetPermission: %v", err) return } } } if strings.Contains(permission, "W") { a.Actions = append(a.Actions, "push") } if strings.Contains(permission, "M") { a.Actions = append(a.Actions, "*") } if strings.Contains(permission, "R") || dao.IsProjectPublic(projectName) { a.Actions = append(a.Actions, "pull") } } } log.Infof("current access, type: %s, name:%s, actions:%v \n", a.Type, a.Name, a.Actions) }
// GetUserIDForRequest tries to get user ID from basic auth header and session. // It returns the user ID, whether need further verification(when the id is from session) and if the action is successful func (b *BaseAPI) GetUserIDForRequest() (int, bool, bool) { username, password, ok := b.Ctx.Request.BasicAuth() if ok { log.Infof("Requst with Basic Authentication header, username: %s", username) user, err := auth.Login(models.AuthModel{ Principal: username, Password: password, }) if err != nil { log.Errorf("Error while trying to login, username: %s, error: %v", username, err) user = nil } if user != nil { // User login successfully no further check required. return user.UserID, false, true } } sessionUserID, ok := b.GetSession("userId").(int) if ok { // The ID is from session return sessionUserID, true, true } log.Debug("No valid user id in session.") return 0, false, false }
// Register add different authenticators to registry map. func Register(name string, authenticator Authenticator) { if _, dup := registry[name]; dup { log.Infof("authenticator: %s has been registered", name) return } registry[name] = authenticator }
func testForMySQL(m *testing.M) int { db := os.Getenv("DATABASE") defer os.Setenv("DATABASE", db) os.Setenv("DATABASE", "mysql") dbHost := os.Getenv("DB_HOST") if len(dbHost) == 0 { log.Fatalf("environment variable DB_HOST is not set") } dbUser := os.Getenv("DB_USR") if len(dbUser) == 0 { log.Fatalf("environment variable DB_USR is not set") } dbPort := os.Getenv("DB_PORT") if len(dbPort) == 0 { log.Fatalf("environment variable DB_PORT is not set") } dbPassword := os.Getenv("DB_PWD") log.Infof("DB_HOST: %s, DB_USR: %s, DB_PORT: %s, DB_PWD: %s\n", dbHost, dbUser, dbPort, dbPassword) os.Setenv("MYSQL_HOST", dbHost) os.Setenv("MYSQL_PORT", dbPort) os.Setenv("MYSQL_USR", dbUser) os.Setenv("MYSQL_PWD", dbPassword) return testForAll(m) }
func init() { //conf/app.conf -> os.Getenv("config_path") configPath := os.Getenv("CONFIG_PATH") if len(configPath) != 0 { log.Infof("Config path: %s", configPath) beego.LoadAppConfig("ini", configPath) } beego.AddFuncMap("i18n", i18n.Tr) langs := strings.Split(beego.AppConfig.String("lang::types"), "|") names := strings.Split(beego.AppConfig.String("lang::names"), "|") supportLanguages = make(map[string]langType) langTypes = make([]*langType, 0, len(langs)) for i, lang := range langs { t := langType{ Lang: lang, Name: names[i], } langTypes = append(langTypes, &t) supportLanguages[lang] = t if err := i18n.SetMessage(lang, "static/i18n/"+"locale_"+lang+".ini"); err != nil { log.Errorf("Fail to set message file: %s", err.Error()) } } }
// 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) }
// UpdateEnablement changes the enablement of the policy func (pa *RepPolicyAPI) UpdateEnablement() { id := pa.GetIDFromURL() policy, err := dao.GetRepPolicy(id) if err != nil { log.Errorf("failed to get policy %d: %v", id, err) pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) } if policy == nil { pa.CustomAbort(http.StatusNotFound, http.StatusText(http.StatusNotFound)) } e := enablementReq{} pa.DecodeJSONReq(&e) if e.Enabled != 0 && e.Enabled != 1 { pa.RenderError(http.StatusBadRequest, "invalid enabled value") return } if policy.Enabled == e.Enabled { return } if err := dao.UpdateRepPolicyEnablement(id, e.Enabled); err != nil { log.Errorf("Failed to update policy enablement in DB, error: %v", err) pa.RenderError(http.StatusInternalServerError, "Internal Error") return } if e.Enabled == 1 { go func() { if err := TriggerReplication(id, "", nil, models.RepOpTransfer); err != nil { log.Errorf("failed to trigger replication of %d: %v", id, err) } else { log.Infof("replication of %d triggered", id) } }() } else { go func() { if err := postReplicationAction(id, "stop"); err != nil { log.Errorf("failed to stop replication of %d: %v", id, err) } else { log.Infof("try to stop replication of %d", id) } }() } }
// InitDatabase initializes the database func InitDatabase() { database, err := getDatabase() if err != nil { panic(err) } log.Infof("initializing database: %s", database.String()) if err := database.Register(); err != nil { panic(err) } }
func initDatabaseForTest() { database, err := getDatabase() if err != nil { panic(err) } log.Infof("initializing database: %s", database.String()) alias := database.Name() if !defaultRegistered { defaultRegistered = true alias = "default" } if err := database.Register(alias); err != nil { panic(err) } }
// TriggerReplicationByRepository triggers the replication according to the repository func TriggerReplicationByRepository(repository string, tags []string, operation string) { policies, err := GetPoliciesByRepository(repository) if err != nil { log.Errorf("failed to get policies for repository %s: %v", repository, err) return } for _, policy := range policies { if policy.Enabled == 0 { continue } if err := TriggerReplication(policy.ID, repository, tags, operation); err != nil { log.Errorf("failed to trigger replication of policy %d for %s: %v", policy.ID, repository, err) } else { log.Infof("replication of policy %d for %s triggered", policy.ID, repository) } } }
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) }
func TestMain(m *testing.M) { databases := []string{"mysql", "sqlite"} for _, database := range databases { log.Infof("run test cases for database: %s", database) result := 1 switch database { case "mysql": result = testForMySQL(m) case "sqlite": result = testForSQLite(m) default: log.Fatalf("invalid database: %s", database) } if result != 0 { os.Exit(result) } } }
// Put modifies name, description, target and enablement of policy func (pa *RepPolicyAPI) Put() { id := pa.GetIDFromURL() originalPolicy, err := dao.GetRepPolicy(id) if err != nil { log.Errorf("failed to get policy %d: %v", id, err) pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) } if originalPolicy == nil { pa.CustomAbort(http.StatusNotFound, http.StatusText(http.StatusNotFound)) } policy := &models.RepPolicy{} pa.DecodeJSONReq(policy) policy.ProjectID = originalPolicy.ProjectID pa.Validate(policy) /* // check duplicate name if policy.Name != originalPolicy.Name { po, err := dao.GetRepPolicyByName(policy.Name) if err != nil { log.Errorf("failed to get policy %s: %v", policy.Name, err) pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) } if po != nil { pa.CustomAbort(http.StatusConflict, "name is already used") } } */ if policy.TargetID != originalPolicy.TargetID { //target of policy can not be modified when the policy is enabled if originalPolicy.Enabled == 1 { pa.CustomAbort(http.StatusBadRequest, "target of policy can not be modified when the policy is enabled") } // check the existance of target target, err := dao.GetRepTarget(policy.TargetID) if err != nil { log.Errorf("failed to get target %d: %v", policy.TargetID, err) pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) } if target == nil { pa.CustomAbort(http.StatusBadRequest, fmt.Sprintf("target %d does not exist", policy.TargetID)) } // check duplicate policy with the same project and target policies, err := dao.GetRepPolicyByProjectAndTarget(policy.ProjectID, policy.TargetID) if err != nil { log.Errorf("failed to get policy [project ID: %d,targetID: %d]: %v", policy.ProjectID, policy.TargetID, err) pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) } if len(policies) > 0 { pa.CustomAbort(http.StatusConflict, "policy already exists with the same project and target") } } policy.ID = id /* isTargetChanged := !(policy.TargetID == originalPolicy.TargetID) isEnablementChanged := !(policy.Enabled == policy.Enabled) var shouldStop, shouldTrigger bool // if target and enablement are not changed, do nothing if !isTargetChanged && !isEnablementChanged { shouldStop = false shouldTrigger = false } else if !isTargetChanged && isEnablementChanged { // target is not changed, but enablement is changed if policy.Enabled == 0 { shouldStop = true shouldTrigger = false } else { shouldStop = false shouldTrigger = true } } else if isTargetChanged && !isEnablementChanged { // target is changed, but enablement is not changed if policy.Enabled == 0 { // enablement is 0, do nothing shouldStop = false shouldTrigger = false } else { // enablement is 1, so stop original target's jobs // and trigger new target's jobs shouldStop = true shouldTrigger = true } } else { // both target and enablement are changed // enablement: 1 -> 0 if policy.Enabled == 0 { shouldStop = true shouldTrigger = false } else { shouldStop = false shouldTrigger = true } } if shouldStop { if err := postReplicationAction(id, "stop"); err != nil { log.Errorf("failed to stop replication of %d: %v", id, err) pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) } log.Infof("replication of %d has been stopped", id) } if err = dao.UpdateRepPolicy(policy); err != nil { log.Errorf("failed to update policy %d: %v", id, err) pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) } if shouldTrigger { go func() { if err := TriggerReplication(id, "", nil, models.RepOpTransfer); err != nil { log.Errorf("failed to trigger replication of %d: %v", id, err) } else { log.Infof("replication of %d triggered", id) } }() } */ if err = dao.UpdateRepPolicy(policy); err != nil { log.Errorf("failed to update policy %d: %v", id, err) pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) } if policy.Enabled != originalPolicy.Enabled && policy.Enabled == 1 { go func() { if err := TriggerReplication(id, "", nil, models.RepOpTransfer); err != nil { log.Errorf("failed to trigger replication of %d: %v", id, err) } else { log.Infof("replication of %d triggered", id) } }() } }
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: ******") }
// Post creates a policy, and if it is enbled, the replication will be triggered right now. func (pa *RepPolicyAPI) Post() { policy := &models.RepPolicy{} pa.DecodeJSONReqAndValidate(policy) /* po, err := dao.GetRepPolicyByName(policy.Name) if err != nil { log.Errorf("failed to get policy %s: %v", policy.Name, err) pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) } if po != nil { pa.CustomAbort(http.StatusConflict, "name is already used") } */ project, err := dao.GetProjectByID(policy.ProjectID) if err != nil { log.Errorf("failed to get project %d: %v", policy.ProjectID, err) pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) } if project == nil { pa.CustomAbort(http.StatusBadRequest, fmt.Sprintf("project %d does not exist", policy.ProjectID)) } target, err := dao.GetRepTarget(policy.TargetID) if err != nil { log.Errorf("failed to get target %d: %v", policy.TargetID, err) pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) } if target == nil { pa.CustomAbort(http.StatusBadRequest, fmt.Sprintf("target %d does not exist", policy.TargetID)) } policies, err := dao.GetRepPolicyByProjectAndTarget(policy.ProjectID, policy.TargetID) if err != nil { log.Errorf("failed to get policy [project ID: %d,targetID: %d]: %v", policy.ProjectID, policy.TargetID, err) pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)) } if len(policies) > 0 { pa.CustomAbort(http.StatusConflict, "policy already exists with the same project and target") } pid, err := dao.AddRepPolicy(*policy) if err != nil { log.Errorf("Failed to add policy to DB, error: %v", err) pa.RenderError(http.StatusInternalServerError, "Internal Error") return } if policy.Enabled == 1 { go func() { if err := TriggerReplication(pid, "", nil, models.RepOpTransfer); err != nil { log.Errorf("failed to trigger replication of %d: %v", pid, err) } else { log.Infof("replication of %d triggered", pid) } }() } pa.Redirect(http.StatusCreated, strconv.FormatInt(pid, 10)) }
// Delete ... func (ra *RepositoryAPI) Delete() { repoName := ra.GetString("repo_name") if len(repoName) == 0 { ra.CustomAbort(http.StatusBadRequest, "repo_name is nil") } projectName, _ := utils.ParseRepository(repoName) project, err := dao.GetProjectByName(projectName) if err != nil { log.Errorf("failed to get project %s: %v", projectName, err) ra.CustomAbort(http.StatusInternalServerError, "") } if project == nil { ra.CustomAbort(http.StatusNotFound, fmt.Sprintf("project %s not found", projectName)) } if project.Public == 0 { userID := ra.ValidateUser() if !hasProjectAdminRole(userID, project.ProjectID) { ra.CustomAbort(http.StatusForbidden, "") } } rc, err := ra.initRepositoryClient(repoName) if err != nil { log.Errorf("error occurred while initializing repository client for %s: %v", repoName, err) ra.CustomAbort(http.StatusInternalServerError, "internal error") } tags := []string{} tag := ra.GetString("tag") if len(tag) == 0 { tagList, err := rc.ListTag() if err != nil { if regErr, ok := err.(*registry_error.Error); ok { ra.CustomAbort(regErr.StatusCode, regErr.Detail) } log.Errorf("error occurred while listing tags of %s: %v", repoName, err) ra.CustomAbort(http.StatusInternalServerError, "internal error") } // TODO remove the logic if the bug of registry is fixed if len(tagList) == 0 { ra.CustomAbort(http.StatusNotFound, http.StatusText(http.StatusNotFound)) } tags = append(tags, tagList...) } else { tags = append(tags, tag) } user, _, ok := ra.Ctx.Request.BasicAuth() if !ok { user, err = ra.getUsername() if err != nil { log.Errorf("failed to get user: %v", err) } } for _, t := range tags { if err := rc.DeleteTag(t); err != nil { if regErr, ok := err.(*registry_error.Error); ok { if regErr.StatusCode != http.StatusNotFound { ra.CustomAbort(regErr.StatusCode, regErr.Detail) } } else { log.Errorf("error occurred while deleting tag %s:%s: %v", repoName, t, err) ra.CustomAbort(http.StatusInternalServerError, "internal error") } } log.Infof("delete tag: %s:%s", repoName, t) go TriggerReplicationByRepository(repoName, []string{t}, models.RepOpDelete) go func(tag string) { if err := dao.AccessLog(user, projectName, repoName, tag, "delete"); err != nil { log.Errorf("failed to add access log: %v", err) } }(t) } exist, err := repositoryExist(repoName, rc) if err != nil { log.Errorf("failed to check the existence of repository %s: %v", repoName, err) ra.CustomAbort(http.StatusInternalServerError, "") } if !exist { if err = dao.DeleteRepository(repoName); err != nil { log.Errorf("failed to delete repository %s: %v", repoName, err) ra.CustomAbort(http.StatusInternalServerError, "") } } go func() { log.Debug("refreshing catalog cache") if err := cache.RefreshCatalogCache(); err != nil { log.Errorf("error occurred while refresh catalog cache: %v", err) } }() }
func init() { expiration = config.TokenExpiration() log.Infof("token expiration: %d minutes", expiration) }