Sfoglia il codice sorgente

add emmil to user collaborator list

Alexander Belanger 4 anni fa
parent
commit
8416632b5f

+ 17 - 0
docs/guides/authorization-and-team-management.md

@@ -0,0 +1,17 @@
+
+Porter supports setting basic authorization permissions via for other members in a Porter project. At the moment, there are 3 roles that can be assigned in a Porter project:
+- **Admin:** read/write access to all resources, ability to delete the project and manage team members.
+- **Developer:** read/write access to applications, jobs, environment groups, cluster data, and integrations. 
+- **Viewer:** read access to applications, jobs, environment groups, and cluster data. 
+
+# Adding Collaborators
+
+To add a new collaborator to a Porter project, you must be logged in with an **Admin** role. As an admin, you will see a **Settings** tab in the sidebar. Navigate to **Settings** and input the email of the user you would like to add. This will generate an invitation link for the user, which expires in 24 hours. The user will get an email to join the Porter project, but if the email is not delivered, you can copy the invite link and send it to them directly. 
+
+TODO: ADD SCREENSHOT
+
+TODO: ADD NOTE ABOUT LOGGING IN AND ACCEPTING INVITE
+
+# Changing Collaborator Permissions
+
+# Removing Collaborators

+ 30 - 0
internal/repository/gorm/helpers_test.go

@@ -113,6 +113,36 @@ func initUser(tester *tester, t *testing.T) {
 	tester.initUsers = append(tester.initUsers, user)
 }
 
+func initMultiUser(tester *tester, t *testing.T) {
+	t.Helper()
+
+	user := &models.User{
+		Email:    "example@example.com",
+		Password: "hello1234",
+	}
+
+	user, err := tester.repo.User.CreateUser(user)
+
+	if err != nil {
+		t.Fatalf("%v\n", err)
+	}
+
+	tester.initUsers = append(tester.initUsers, user)
+
+	user = &models.User{
+		Email:    "example2@example.com",
+		Password: "hello1234",
+	}
+
+	user, err = tester.repo.User.CreateUser(user)
+
+	if err != nil {
+		t.Fatalf("%v\n", err)
+	}
+
+	tester.initUsers = append(tester.initUsers, user)
+}
+
 func initProject(tester *tester, t *testing.T) {
 	t.Helper()
 

+ 11 - 0
internal/repository/gorm/user.go

@@ -35,6 +35,17 @@ func (repo *UserRepository) ReadUser(id uint) (*models.User, error) {
 	return user, nil
 }
 
+// ListUsersByIDs finds all users matching ids
+func (repo *UserRepository) ListUsersByIDs(ids []uint) ([]*models.User, error) {
+	users := make([]*models.User, 0)
+
+	if err := repo.db.Model(&models.User{}).Where("id IN (?)", ids).Find(&users).Error; err != nil {
+		return nil, err
+	}
+
+	return users, nil
+}
+
 // ReadUserByEmail finds a single user based on their unique email
 func (repo *UserRepository) ReadUserByEmail(email string) (*models.User, error) {
 	user := &models.User{}

+ 34 - 0
internal/repository/gorm/user_test.go

@@ -7,6 +7,40 @@ import (
 	"github.com/porter-dev/porter/internal/models"
 )
 
+func TestListUsersByIDs(t *testing.T) {
+	tester := &tester{
+		dbFileName: "./porter_list_users_by_ids.db",
+	}
+
+	setupTestEnv(tester, t)
+	initMultiUser(tester, t)
+	defer cleanup(tester, t)
+
+	users, err := tester.repo.User.ListUsersByIDs([]uint{1, 2})
+
+	if err != nil {
+		t.Fatalf("%v\n", err)
+	}
+
+	if diff := deep.Equal(tester.initUsers, users); diff != nil {
+		t.Errorf("users not equal:")
+		t.Error(diff)
+	}
+
+	users, err = tester.repo.User.ListUsersByIDs([]uint{1})
+
+	if err != nil {
+		t.Fatalf("%v\n", err)
+	}
+
+	expUsers := []*models.User{tester.initUsers[0]}
+
+	if diff := deep.Equal(expUsers, users); diff != nil {
+		t.Errorf("users not equal:")
+		t.Error(diff)
+	}
+}
+
 func TestReadUserByGithubUserID(t *testing.T) {
 	tester := &tester{
 		dbFileName: "./porter_read_user_github.db",

+ 19 - 0
internal/repository/memory/user.go

@@ -56,6 +56,25 @@ func (repo *UserRepository) ReadUser(id uint) (*models.User, error) {
 	return repo.users[index], nil
 }
 
+func (repo *UserRepository) ListUsersByIDs(ids []uint) ([]*models.User, error) {
+	if !repo.canQuery {
+		return nil, errors.New("Cannot read from database")
+	}
+
+	resp := make([]*models.User, 0)
+
+	// find all roles matching
+	for _, user := range repo.users {
+		for _, userID := range ids {
+			if userID == user.ID {
+				resp = append(resp, user)
+			}
+		}
+	}
+
+	return resp, nil
+}
+
 // ReadUserByEmail finds a single user based on their unique email
 func (repo *UserRepository) ReadUserByEmail(email string) (*models.User, error) {
 	if !repo.canQuery {

+ 1 - 0
internal/repository/user.go

@@ -15,6 +15,7 @@ type UserRepository interface {
 	ReadUserByEmail(email string) (*models.User, error)
 	ReadUserByGithubUserID(id int64) (*models.User, error)
 	ReadUserByGoogleUserID(id string) (*models.User, error)
+	ListUsersByIDs(ids []uint) ([]*models.User, error)
 	UpdateUser(user *models.User) (*models.User, error)
 	DeleteUser(user *models.User) (*models.User, error)
 }

+ 30 - 2
server/api/project_handler.go

@@ -97,6 +97,14 @@ func (app *App) HandleGetProjectRoles(w http.ResponseWriter, r *http.Request) {
 	}
 }
 
+type Collaborator struct {
+	ID        uint   `json:"id"`
+	Kind      string `json:"kind"`
+	UserID    uint   `json:"user_id"`
+	Email     string `json:"email"`
+	ProjectID uint   `json:"project_id"`
+}
+
 // HandleListProjectCollaborators lists the collaborators in the project
 func (app *App) HandleListProjectCollaborators(w http.ResponseWriter, r *http.Request) {
 	id, err := strconv.ParseUint(chi.URLParam(r, "project_id"), 0, 64)
@@ -113,10 +121,30 @@ func (app *App) HandleListProjectCollaborators(w http.ResponseWriter, r *http.Re
 		return
 	}
 
-	res := make([]*models.Role, 0)
+	res := make([]*Collaborator, 0)
+	roleMap := make(map[uint]*models.Role)
+	idArr := make([]uint, 0)
 
 	for _, role := range roles {
-		res = append(res, &role)
+		roleMap[role.UserID] = &role
+		idArr = append(idArr, role.UserID)
+	}
+
+	users, err := app.Repo.User.ListUsersByIDs(idArr)
+
+	if err != nil {
+		app.handleErrorRead(err, ErrProjectDataRead, w)
+		return
+	}
+
+	for _, user := range users {
+		res = append(res, &Collaborator{
+			ID:        roleMap[user.ID].ID,
+			Kind:      roleMap[user.ID].Kind,
+			UserID:    roleMap[user.ID].UserID,
+			Email:     user.Email,
+			ProjectID: roleMap[user.ID].ProjectID,
+		})
 	}
 
 	w.WriteHeader(http.StatusOK)