feat: team access control

This commit is contained in:
2024-11-12 17:17:10 +00:00
parent f5250d5361
commit 2d4c81e15d
31 changed files with 410 additions and 161 deletions
+21 -13
View File
@@ -1,6 +1,8 @@
package models
import "gorm.io/datatypes"
import (
"gorm.io/datatypes"
)
const (
HostTypeSSH = "ssh"
@@ -14,8 +16,10 @@ const (
type Host struct {
Model
OwnerID string `json:"userId" gorm:"index:hosts_owner_id_idx;type:varchar(26)"`
Owner User `json:"user" gorm:"foreignKey:OwnerID"`
OwnerID *string `json:"userId" gorm:"type:varchar(26)"`
Owner *User `json:"user" gorm:"foreignKey:OwnerID"`
TeamID *string `json:"teamId" gorm:"type:varchar(26)"`
Team *Team `json:"team" gorm:"foreignKey:TeamID"`
Type string `json:"type" gorm:"not null;index:hosts_type_idx;type:varchar(16)"`
Label string `json:"label"`
@@ -24,11 +28,11 @@ type Host struct {
OS string `json:"os" gorm:"type:varchar(32)"`
Metadata datatypes.JSONMap `json:"metadata"`
ParentID *string `json:"parentId" gorm:"index:hosts_parent_id_idx;type:varchar(26)"`
ParentID *string `json:"parentId" gorm:"type:varchar(26)"`
Parent *Host `json:"parent" gorm:"foreignKey:ParentID"`
KeyID *string `json:"keyId" gorm:"index:hosts_key_id_idx"`
KeyID *string `json:"keyId" gorm:"type:varchar(26)"`
Key Keychain `json:"key" gorm:"foreignKey:KeyID"`
AltKeyID *string `json:"altKeyId" gorm:"index:hosts_altkey_id_idx"`
AltKeyID *string `json:"altKeyId" gorm:"type:varchar(26)"`
AltKey Keychain `json:"altKey" gorm:"foreignKey:AltKeyID"`
Timestamps
@@ -58,13 +62,17 @@ func (h *Host) DecryptKeys() (*HostDecrypted, error) {
return res, nil
}
type HostHasAccessOptions struct {
UserID string
}
func (h *Host) HasAccess(o HostHasAccessOptions) bool {
if o.UserID == h.OwnerID {
func (h *Host) HasAccess(user *User) bool {
if user.IsAdmin() {
return true
}
return false
return *h.OwnerID == user.ID || user.IsInTeam(h.TeamID)
}
func (h *Host) CanWrite(user *User) bool {
if user.IsAdmin() {
return true
}
teamRole := user.GetTeamRole(h.TeamID)
return *h.OwnerID == user.ID || teamRole == TeamRoleOwner || teamRole == TeamRoleAdmin
}
+19 -2
View File
@@ -16,8 +16,10 @@ const (
type Keychain struct {
Model
OwnerID string `json:"userId" gorm:"index:hosts_owner_id_idx;type:varchar(26)"`
Owner User `json:"user" gorm:"foreignKey:OwnerID"`
OwnerID *string `json:"userId" gorm:"type:varchar(26)"`
Owner *User `json:"user" gorm:"foreignKey:OwnerID"`
TeamID *string `json:"teamId" gorm:"type:varchar(26)"`
Team *Team `json:"team" gorm:"foreignKey:TeamID"`
Label string `json:"label"`
Type string `json:"type" gorm:"not null;index:keychains_type_idx;type:varchar(12)"`
@@ -55,3 +57,18 @@ func (k *Keychain) DecryptData(data interface{}) error {
return nil
}
func (k *Keychain) HasAccess(user *User) bool {
if user.IsAdmin() {
return true
}
return *k.OwnerID == user.ID || user.IsInTeam(k.TeamID)
}
func (k *Keychain) CanWrite(user *User) bool {
if user.IsAdmin() {
return true
}
teamRole := user.GetTeamRole(k.TeamID)
return *k.OwnerID == user.ID || teamRole == TeamRoleOwner || teamRole == TeamRoleAdmin
}
+9 -3
View File
@@ -2,12 +2,18 @@ package models
import "time"
const (
TeamRoleOwner = "owner"
TeamRoleAdmin = "admin"
TeamRoleMember = "member"
)
type Team struct {
Model
Name string `json:"name" gorm:"type:varchar(32)"`
Icon string `json:"icon" gorm:"type:varchar(2)"`
Members []*User `json:"members" gorm:"many2many:team_members"`
Name string `json:"name" gorm:"type:varchar(32)"`
Icon string `json:"icon" gorm:"type:varchar(2)"`
Members []*TeamMembers `json:"members" gorm:"foreignKey:TeamID"`
Timestamps
SoftDeletes
+33 -1
View File
@@ -1,5 +1,7 @@
package models
import "slices"
const (
UserRoleUser = "user"
UserRoleAdmin = "admin"
@@ -14,7 +16,7 @@ type User struct {
Email string `json:"email" gorm:"unique"`
Role string `json:"role" gorm:"default:user;not null;index:users_role_idx;type:varchar(8)"`
Teams []*Team `json:"teams" gorm:"many2many:team_members"`
Teams []*TeamMembers `json:"teams" gorm:"foreignKey:UserID"`
Timestamps
SoftDeletes
@@ -28,3 +30,33 @@ type UserSession struct {
Timestamps
SoftDeletes
}
func (u *User) IsAdmin() bool {
return u.Role == UserRoleAdmin
}
func (u *User) GetTeamRole(teamId *string) string {
if u.IsAdmin() {
return TeamRoleAdmin
}
if teamId == nil {
return ""
}
idx := slices.IndexFunc(u.Teams, func(tm *TeamMembers) bool {
return tm.TeamID == *teamId
})
if idx == -1 {
return ""
}
return u.Teams[idx].Role
}
func (u *User) IsInTeam(teamId *string) bool {
role := u.GetTeamRole(teamId)
return role != ""
}
func (u *User) TeamCanWrite(teamId *string) bool {
role := u.GetTeamRole(teamId)
return role == TeamRoleAdmin || role == TeamRoleOwner
}