func addUserToTeam(email, teamName string, u *User) error { team, user := new(Team), new(User) selector := bson.M{"_id": teamName} err := db.Session.Teams().Find(selector).One(team) if err != nil { return &errors.Http{Code: http.StatusNotFound, Message: "Team not found"} } if !team.containsUser(u) { msg := fmt.Sprintf("You are not authorized to add new users to the team %s", team.Name) return &errors.Http{Code: http.StatusUnauthorized, Message: msg} } err = db.Session.Users().Find(bson.M{"email": email}).One(user) if err != nil { return &errors.Http{Code: http.StatusNotFound, Message: "User not found"} } // does not touches the database err = team.addUser(user) if err != nil { return &errors.Http{Code: http.StatusConflict, Message: err.Error()} } gUrl := repository.GitServerUri() alwdApps, err := allowedApps(u.Email) if err := (&gandalf.Client{Endpoint: gUrl}).GrantAccess(alwdApps, []string{email}); err != nil { return err } return db.Session.Teams().Update(selector, team) }
func revokeAccessFromTeam(appName, teamName string, u *auth.User) error { t := new(auth.Team) app, err := getAppOrError(appName, u) if err != nil { return err } err = db.Session.Teams().Find(bson.M{"_id": teamName}).One(t) if err != nil { return &errors.Http{Code: http.StatusNotFound, Message: "Team not found"} } if len(app.Teams) == 1 { msg := "You can not revoke the access from this team, because it is the unique team with access to the app, and an app can not be orphaned" return &errors.Http{Code: http.StatusForbidden, Message: msg} } err = app.Revoke(t) if err != nil { return &errors.Http{Code: http.StatusNotFound, Message: err.Error()} } err = db.Session.Apps().Update(bson.M{"name": app.Name}, app) if err != nil { return err } users := getEmailsForRevoking(&app, t) if len(users) > 0 { gUrl := repository.GitServerUri() if err := (&gandalf.Client{Endpoint: gUrl}).RevokeAccess([]string{app.Name}, users); err != nil { return &errors.Http{Code: http.StatusInternalServerError, Message: err.Error()} } } return nil }
func CreateUser(w http.ResponseWriter, r *http.Request) error { var u auth.User err := json.NewDecoder(r.Body).Decode(&u) if err != nil { return &errors.Http{Code: http.StatusBadRequest, Message: err.Error()} } if !validation.ValidateEmail(u.Email) { return &errors.Http{Code: http.StatusPreconditionFailed, Message: emailError} } if !validation.ValidateLength(u.Password, passwordMinLen, passwordMaxLen) { return &errors.Http{Code: http.StatusPreconditionFailed, Message: passwordError} } gUrl := repository.GitServerUri() c := gandalf.Client{Endpoint: gUrl} if _, err := c.NewUser(u.Email, keyToMap(u.Keys)); err != nil { return fmt.Errorf("Failed to create user in the git server: %s", err) } if err := u.Create(); err == nil { w.WriteHeader(http.StatusCreated) return nil } if u.Get() == nil { err = &errors.Http{Code: http.StatusConflict, Message: "This email is already registered"} } return err }
func removeUserFromTeam(email, teamName string, u *User) error { team := new(Team) err := db.Session.Teams().FindId(teamName).One(team) if err != nil { return &errors.Http{Code: http.StatusNotFound, Message: "Team not found"} } if !team.containsUser(u) { msg := fmt.Sprintf("You are not authorized to remove a member from the team %s", team.Name) return &errors.Http{Code: http.StatusUnauthorized, Message: msg} } if len(team.Users) == 1 { msg := "You can not remove this user from this team, because it is the last user within the team, and a team can not be orphaned" return &errors.Http{Code: http.StatusForbidden, Message: msg} } user := User{Email: email} err = user.Get() if err != nil { return &errors.Http{Code: http.StatusNotFound, Message: err.Error()} } // does not touches the database err = team.removeUser(&user) if err != nil { return &errors.Http{Code: http.StatusNotFound, Message: err.Error()} } // gandalf actions comes first, cuz if they fail the whole action is aborted gUrl := repository.GitServerUri() alwdApps, err := allowedApps(email) if err != nil { return err } if err := (&gandalf.Client{Endpoint: gUrl}).RevokeAccess(alwdApps, []string{email}); err != nil { return err } return db.Session.Teams().UpdateId(teamName, team) }
func grantAccessToTeam(appName, teamName string, u *auth.User) error { t := new(auth.Team) app, err := getApp(appName, u) if err != nil { return err } conn, err := db.Conn() if err != nil { return err } defer conn.Close() err = conn.Teams().Find(bson.M{"_id": teamName}).One(t) if err != nil { return &errors.Http{Code: http.StatusNotFound, Message: "Team not found"} } err = app.Grant(t) if err != nil { return &errors.Http{Code: http.StatusConflict, Message: err.Error()} } err = conn.Apps().Update(bson.M{"name": app.Name}, app) if err != nil { return err } gUrl := repository.GitServerUri() gClient := gandalf.Client{Endpoint: gUrl} if err := gClient.GrantAccess([]string{app.Name}, t.Users); err != nil { return fmt.Errorf("Failed to grant access in the git server: %s.", err) } return nil }
func removeKeyFromGandalf(key *auth.Key, u *auth.User) error { gUrl := repository.GitServerUri() if err := (&gandalf.Client{Endpoint: gUrl}).RemoveKey(u.Email, key.Name); err != nil { return fmt.Errorf("Failed to remove the key from git server: %s", err) } return nil }
func addKeyInGandalf(key *auth.Key, u *auth.User) error { key.Name = fmt.Sprintf("%s-%d", u.Email, len(u.Keys)+1) gUrl := repository.GitServerUri() if err := (&gandalf.Client{Endpoint: gUrl}).AddKey(u.Email, keyToMap([]auth.Key{*key})); err != nil { return fmt.Errorf("Failed to add key to git server: %s", err) } return nil }
// createRepository forward creates a git repository using the // gandalf client. func (a *createRepository) forward(app *App, args ...interface{}) error { gUrl := repository.GitServerUri() var users []string for _, t := range app.GetTeams() { users = append(users, t.Users...) } c := gandalf.Client{Endpoint: gUrl} _, err := c.NewRepository(app.Name, users, false) return err }
func addUserToTeamInGandalf(email string, u *auth.User, t *auth.Team) error { gUrl := repository.GitServerUri() alwdApps, err := u.AllowedApps() if err != nil { return fmt.Errorf("Failed to obtain allowed apps to grant: %s", err.Error()) } if err := (&gandalf.Client{Endpoint: gUrl}).GrantAccess(alwdApps, []string{email}); err != nil { return fmt.Errorf("Failed to grant access to git repositories: %s", err) } return nil }
func removeUserFromTeamInGandalf(u *auth.User, team string) error { gUrl := repository.GitServerUri() alwdApps, err := u.AllowedAppsByTeam(team) if err != nil { return err } if err := (&gandalf.Client{Endpoint: gUrl}).RevokeAccess(alwdApps, []string{u.Email}); err != nil { return fmt.Errorf("Failed to revoke access from git repositories: %s", err) } return nil }
// revomeKeyFromUser removes a key from the given user's document // // Also removes the key from gandalf. // When we were using gitosis we had to revoke the write permission into the repositories in this moment, // now that we are using gandalf, it is not necessary anymore, this is done by addUserToTeam // // This functions makes uses of git:host, git:protocol and optionaly git:port configurations func removeKeyFromUser(content string, u *User) error { key, index := u.findKey(Key{Content: content}) if index < 0 { return &errors.Http{Code: http.StatusNotFound, Message: "User does not have this key"} } gUrl := repository.GitServerUri() if err := (&gandalf.Client{Endpoint: gUrl}).RemoveKey(u.Email, key.Name); err != nil { return err } u.removeKey(key) return db.Session.Users().Update(bson.M{"email": u.Email}, u) }
// addKeyToUser adds a key to a user in mongodb and send the key to the git server // in order to allow ssh-ing into git server. // // While using gitosis, we had to give write permission to the user into a repository // in the same moment we add their key, with gandalf it is not needed anymore, thus here we just // add the key to the user, the grant step is done in user creation time func addKeyToUser(content string, u *User) error { key := Key{Content: content} if u.hasKey(key) { return &errors.Http{Code: http.StatusConflict, Message: "User has this key already"} } key.Name = fmt.Sprintf("%s-%d", u.Email, len(u.Keys)+1) gUrl := repository.GitServerUri() u.addKey(key) if err := (&gandalf.Client{Endpoint: gUrl}).AddKey(u.Email, keyToMap(u.Keys)); err != nil { return err } return db.Session.Users().Update(bson.M{"email": u.Email}, u) }
// RemoveUser removes the user from the database and from gandalf server // // In order to successfuly remove a user, it's need that he/she is not the only // one in a team, otherwise the function will return an error. func removeUser(w http.ResponseWriter, r *http.Request, t *auth.Token) error { u, err := t.User() if err != nil { return err } gUrl := repository.GitServerUri() c := gandalf.Client{Endpoint: gUrl} alwdApps, err := u.AllowedApps() if err != nil { return err } if err := c.RevokeAccess(alwdApps, []string{u.Email}); err != nil { log.Printf("Failed to revoke access in Gandalf: %s", err) return fmt.Errorf("Failed to revoke acess from git repositories: %s", err) } teams, err := u.Teams() if err != nil { return err } conn, err := db.Conn() if err != nil { return err } defer conn.Close() for _, team := range teams { if len(team.Users) < 2 { msg := fmt.Sprintf(`This user is the last member of the team "%s", so it cannot be removed. Please remove the team, them remove the user.`, team.Name) return &errors.Http{Code: http.StatusForbidden, Message: msg} } err = team.RemoveUser(u) if err != nil { return err } // this can be done without the loop err = conn.Teams().Update(bson.M{"_id": team.Name}, team) if err != nil { return err } } rec.Log(u.Email, "remove-user") if err := c.RemoveUser(u.Email); err != nil { log.Printf("Failed to remove user from gandalf: %s", err) return fmt.Errorf("Failed to remove the user from the git server: %s", err) } return conn.Users().Remove(bson.M{"email": u.Email}) }
func AppDelete(w http.ResponseWriter, r *http.Request, u *auth.User) error { app, err := getAppOrError(r.URL.Query().Get(":name"), u) if err != nil { return err } gUrl := repository.GitServerUri() if err := (&gandalf.Client{Endpoint: gUrl}).RemoveRepository(app.Name); err != nil { log.Printf("Got error while removing repository from gandalf: %s", err.Error()) return &errors.Http{Code: http.StatusInternalServerError, Message: "Could not remove app's repository at git server. Aborting..."} } if err := app.Destroy(); err != nil { return err } fmt.Fprint(w, "success") return nil }
// ForceDestroy destroys an app with force. // // Destroying an app is a process composed of four steps: // // 1. Destroy the bucket and S3 credentials (if bucket-support is // enabled). // 2. Destroy the app unit using juju // 3. Unbind all service instances from the app // 4. Remove the app from the database func ForceDestroy(app *App) error { gUrl := repository.GitServerUri() (&gandalf.Client{Endpoint: gUrl}).RemoveRepository(app.Name) useS3, _ := config.GetBool("bucket-support") if useS3 { destroyBucket(app) } if len(app.Units) > 0 { Provisioner.Destroy(app) app.unbind() } conn, err := db.Conn() if err != nil { return err } defer conn.Close() return conn.Apps().Remove(bson.M{"name": app.Name}) }
func revokeAppAccess(w http.ResponseWriter, r *http.Request, t *auth.Token) error { u, err := t.User() if err != nil { return err } appName := r.URL.Query().Get(":app") teamName := r.URL.Query().Get(":team") rec.Log(u.Email, "revoke-app-access", "app="+appName, "team="+teamName) team := new(auth.Team) app, err := getApp(appName, u) if err != nil { return err } conn, err := db.Conn() if err != nil { return err } defer conn.Close() err = conn.Teams().Find(bson.M{"_id": teamName}).One(team) if err != nil { return &errors.Http{Code: http.StatusNotFound, Message: "Team not found"} } if len(app.Teams) == 1 { msg := "You can not revoke the access from this team, because it is the unique team with access to the app, and an app can not be orphaned" return &errors.Http{Code: http.StatusForbidden, Message: msg} } err = app.Revoke(team) if err != nil { return &errors.Http{Code: http.StatusNotFound, Message: err.Error()} } err = conn.Apps().Update(bson.M{"name": app.Name}, app) if err != nil { return err } users := getEmailsForRevoking(&app, team) if len(users) > 0 { gUrl := repository.GitServerUri() if err := (&gandalf.Client{Endpoint: gUrl}).RevokeAccess([]string{app.Name}, users); err != nil { return fmt.Errorf("Failed to revoke access in the git server: %s", err) } } return nil }
func grantAccessToTeam(appName, teamName string, u *auth.User) error { t := new(auth.Team) app, err := getAppOrError(appName, u) if err != nil { return err } err = db.Session.Teams().Find(bson.M{"_id": teamName}).One(t) if err != nil { return &errors.Http{Code: http.StatusNotFound, Message: "Team not found"} } err = app.Grant(t) if err != nil { return &errors.Http{Code: http.StatusConflict, Message: err.Error()} } err = db.Session.Apps().Update(bson.M{"name": app.Name}, app) if err != nil { return err } gUrl := repository.GitServerUri() return (&gandalf.Client{Endpoint: gUrl}).GrantAccess([]string{app.Name}, t.Users) }
func grantAppAccess(w http.ResponseWriter, r *http.Request, t *auth.Token) error { u, err := t.User() if err != nil { return err } appName := r.URL.Query().Get(":app") teamName := r.URL.Query().Get(":team") rec.Log(u.Email, "grant-app-access", "app="+appName, "team="+teamName) team := new(auth.Team) app, err := getApp(appName, u) if err != nil { return err } conn, err := db.Conn() if err != nil { return err } defer conn.Close() err = conn.Teams().Find(bson.M{"_id": teamName}).One(team) if err != nil { return &errors.Http{Code: http.StatusNotFound, Message: "Team not found"} } err = app.Grant(team) if err != nil { return &errors.Http{Code: http.StatusConflict, Message: err.Error()} } err = conn.Apps().Update(bson.M{"name": app.Name}, app) if err != nil { return err } gUrl := repository.GitServerUri() gClient := gandalf.Client{Endpoint: gUrl} if err := gClient.GrantAccess([]string{app.Name}, team.Users); err != nil { return fmt.Errorf("Failed to grant access in the git server: %s.", err) } return nil }
// RemoveUser removes the user from the database and from gandalf server // // In order to successfuly remove a user, it's need that he/she is not the only one in a team, // otherwise the function will return an error // TODO: improve the team update, if possible func RemoveUser(w http.ResponseWriter, r *http.Request, u *User) error { //_, err := db.Session.Teams().UpdateAll(bson.M{"users": u.Email}, bson.M{"$pull": bson.M{"users": u.Email}}) gUrl := repository.GitServerUri() c := gandalf.Client{Endpoint: gUrl} alwdApps, err := allowedApps(u.Email) if err != nil { return err } if err := c.RevokeAccess(alwdApps, []string{u.Email}); err != nil { return err } teams, err := u.Teams() if err != nil { return err } for _, team := range teams { if len(team.Users) < 2 { msg := fmt.Sprintf(`This user is the last member of the team "%s", so it cannot be removed. Please remove the team, them remove the user.`, team.Name) return &errors.Http{Code: http.StatusForbidden, Message: msg} } err = team.removeUser(u) if err != nil { return err } // this can be done without the loop err = db.Session.Teams().Update(bson.M{"_id": team.Name}, team) if err != nil { return err } } if err := c.RemoveUser(u.Email); err != nil { return &errors.Http{Code: http.StatusInternalServerError, Message: "Could not communicate with git server. Aborting..."} } return db.Session.Users().Remove(bson.M{"email": u.Email}) }
// createRepository backward remove the git repository // using the gandalf client. func (a *createRepository) backward(app *App, args ...interface{}) { gUrl := repository.GitServerUri() c := gandalf.Client{Endpoint: gUrl} c.RemoveRepository(app.Name) }
MinParams: 1, } // createRepository creates a repository for the app in Gandalf. var createRepository = action.Action{ Forward: func(ctx action.FWContext) (action.Result, error) { var app App switch ctx.Params[0].(type) { case App: app = ctx.Params[0].(App) case *App: app = *ctx.Params[0].(*App) default: return nil, errors.New("First parameter must be App or *App.") } gUrl := repository.GitServerUri() var users []string for _, t := range app.GetTeams() { users = append(users, t.Users...) } c := gandalf.Client{Endpoint: gUrl} _, err := c.NewRepository(app.Name, users, false) return &app, err }, Backward: func(ctx action.BWContext) { app := ctx.FWResult.(*App) app.Get() gUrl := repository.GitServerUri() c := gandalf.Client{Endpoint: gUrl} c.RemoveRepository(app.Name) },