feat: user roles

This commit is contained in:
Johan Siebens
2022-06-22 07:47:09 +02:00
parent 32c396a972
commit 12eb258e1e
11 changed files with 109 additions and 44 deletions
+11 -3
View File
@@ -19,9 +19,17 @@ type Identity struct {
}
type IAMPolicy struct {
Subs []string `json:"subs,omitempty"`
Emails []string `json:"emails,omitempty"`
Filters []string `json:"filters,omitempty"`
Subs []string `json:"subs,omitempty"`
Emails []string `json:"emails,omitempty"`
Filters []string `json:"filters,omitempty"`
Roles map[string]UserRole `json:"roles,omitempty"`
}
func (i *IAMPolicy) GetRole(user User) UserRole {
if val, ok := i.Roles[user.Name]; ok {
return val
}
return UserRoleMember
}
func (i *IAMPolicy) EvaluatePolicy(identity *Identity) (bool, error) {
+12
View File
@@ -23,6 +23,18 @@ const (
UserTypePerson UserType = "person"
)
type UserRole string
const (
UserRoleNone UserRole = ""
UserRoleMember UserRole = "member"
UserRoleAdmin UserRole = "admin"
)
func (s UserRole) IsAdmin() bool {
return s == UserRoleAdmin
}
type User struct {
ID uint64 `gorm:"primary_key;autoIncrement:false"`
Name string
+2 -2
View File
@@ -12,7 +12,7 @@ import (
func (s *Service) GetACLPolicy(ctx context.Context, req *connect.Request[api.GetACLPolicyRequest]) (*connect.Response[api.GetACLPolicyResponse], error) {
principal := CurrentPrincipal(ctx)
if !principal.IsSystemAdmin() && !principal.TailnetMatches(req.Msg.TailnetId) {
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(req.Msg.TailnetId) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
@@ -34,7 +34,7 @@ func (s *Service) GetACLPolicy(ctx context.Context, req *connect.Request[api.Get
func (s *Service) SetACLPolicy(ctx context.Context, req *connect.Request[api.SetACLPolicyRequest]) (*connect.Response[api.SetACLPolicyResponse], error) {
principal := CurrentPrincipal(ctx)
if !principal.IsSystemAdmin() {
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(req.Msg.TailnetId) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
+4 -4
View File
@@ -22,7 +22,7 @@ func (s *Service) GetAuthKey(ctx context.Context, req *connect.Request[api.GetAu
return nil, connect.NewError(connect.CodeNotFound, errors.New("auth key not found"))
}
if !principal.IsSystemAdmin() && !principal.UserMatches(key.UserID) {
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(key.TailnetID) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
@@ -73,7 +73,7 @@ func mapAuthKeysToApi(authKeys []domain.AuthKey) []*api.AuthKey {
func (s *Service) ListAuthKeys(ctx context.Context, req *connect.Request[api.ListAuthKeysRequest]) (*connect.Response[api.ListAuthKeysResponse], error) {
principal := CurrentPrincipal(ctx)
if !principal.IsSystemAdmin() && !principal.TailnetMatches(req.Msg.TailnetId) {
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(req.Msg.TailnetId) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
@@ -113,7 +113,7 @@ func (s *Service) ListAuthKeys(ctx context.Context, req *connect.Request[api.Lis
func (s *Service) CreateAuthKey(ctx context.Context, req *connect.Request[api.CreateAuthKeyRequest]) (*connect.Response[api.CreateAuthKeyResponse], error) {
principal := CurrentPrincipal(ctx)
if !principal.IsSystemAdmin() && !principal.TailnetMatches(req.Msg.TailnetId) {
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(req.Msg.TailnetId) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
@@ -187,7 +187,7 @@ func (s *Service) DeleteAuthKey(ctx context.Context, req *connect.Request[api.De
return nil, connect.NewError(connect.CodeNotFound, errors.New("auth key not found"))
}
if !principal.IsSystemAdmin() && !principal.UserMatches(key.UserID) {
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(key.UserID) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
+2 -2
View File
@@ -10,7 +10,7 @@ import (
func (s *Service) GetDNSConfig(ctx context.Context, req *connect.Request[api.GetDNSConfigRequest]) (*connect.Response[api.GetDNSConfigResponse], error) {
principal := CurrentPrincipal(ctx)
if !principal.IsSystemAdmin() && !principal.TailnetMatches(req.Msg.TailnetId) {
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(req.Msg.TailnetId) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
@@ -38,7 +38,7 @@ func (s *Service) GetDNSConfig(ctx context.Context, req *connect.Request[api.Get
func (s *Service) SetDNSConfig(ctx context.Context, req *connect.Request[api.SetDNSConfigRequest]) (*connect.Response[api.SetDNSConfigResponse], error) {
principal := CurrentPrincipal(ctx)
if !principal.IsSystemAdmin() {
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(req.Msg.TailnetId) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
+20 -2
View File
@@ -11,7 +11,7 @@ import (
func (s *Service) GetIAMPolicy(ctx context.Context, req *connect.Request[api.GetIAMPolicyRequest]) (*connect.Response[api.GetIAMPolicyResponse], error) {
principal := CurrentPrincipal(ctx)
if !principal.IsSystemAdmin() && !principal.TailnetMatches(req.Msg.TailnetId) {
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(req.Msg.TailnetId) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
@@ -27,6 +27,7 @@ func (s *Service) GetIAMPolicy(ctx context.Context, req *connect.Request[api.Get
Subs: tailnet.IAMPolicy.Subs,
Emails: tailnet.IAMPolicy.Emails,
Filters: tailnet.IAMPolicy.Filters,
Roles: domainRolesMapToApiRolesMap(tailnet.IAMPolicy.Roles),
}
return connect.NewResponse(&api.GetIAMPolicyResponse{Policy: policy}), nil
@@ -34,7 +35,7 @@ func (s *Service) GetIAMPolicy(ctx context.Context, req *connect.Request[api.Get
func (s *Service) SetIAMPolicy(ctx context.Context, req *connect.Request[api.SetIAMPolicyRequest]) (*connect.Response[api.SetIAMPolicyResponse], error) {
principal := CurrentPrincipal(ctx)
if !principal.IsSystemAdmin() {
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(req.Msg.TailnetId) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
@@ -50,6 +51,7 @@ func (s *Service) SetIAMPolicy(ctx context.Context, req *connect.Request[api.Set
Subs: req.Msg.Policy.Subs,
Emails: req.Msg.Policy.Emails,
Filters: req.Msg.Policy.Filters,
Roles: apiRolesMapToDomainRolesMap(req.Msg.Policy.Roles),
}
if err := s.repository.SaveTailnet(ctx, tailnet); err != nil {
@@ -58,3 +60,19 @@ func (s *Service) SetIAMPolicy(ctx context.Context, req *connect.Request[api.Set
return connect.NewResponse(&api.SetIAMPolicyResponse{}), nil
}
func apiRolesMapToDomainRolesMap(values map[string]string) map[string]domain.UserRole {
var result = map[string]domain.UserRole{}
for k, v := range values {
result[k] = domain.UserRole(v)
}
return result
}
func domainRolesMapToApiRolesMap(values map[string]domain.UserRole) map[string]string {
var result = map[string]string{}
for k, v := range values {
result[k] = string(v)
}
return result
}
+12 -3
View File
@@ -21,13 +21,18 @@ const (
type Principal struct {
SystemRole domain.SystemRole
User *domain.User
UserRole domain.UserRole
}
func (p Principal) IsSystemAdmin() bool {
return p.SystemRole.IsAdmin()
}
func (p Principal) TailnetMatches(tailnetID uint64) bool {
func (p Principal) IsTailnetAdmin(tailnetID uint64) bool {
return p.User.TailnetID == tailnetID && p.UserRole.IsAdmin()
}
func (p Principal) IsTailnetMember(tailnetID uint64) bool {
return p.User.TailnetID == tailnetID
}
@@ -38,7 +43,7 @@ func (p Principal) UserMatches(userID uint64) bool {
func CurrentPrincipal(ctx context.Context) Principal {
p := ctx.Value(principalKey)
if p == nil {
return Principal{SystemRole: domain.SystemRoleNone}
return Principal{SystemRole: domain.SystemRoleNone, UserRole: domain.UserRoleNone}
}
return p.(Principal)
}
@@ -81,5 +86,9 @@ func exchangeToken(ctx context.Context, systemAdminKey key.ServerPrivate, reposi
return nil
}
return &Principal{User: &apiKey.User, SystemRole: domain.SystemRoleNone}
user := apiKey.User
tailnet := apiKey.Tailnet
role := tailnet.IAMPolicy.GetRole(user)
return &Principal{User: &apiKey.User, SystemRole: domain.SystemRoleNone, UserRole: role}
}
+6 -6
View File
@@ -13,7 +13,7 @@ import (
func (s *Service) ListMachines(ctx context.Context, req *connect.Request[api.ListMachinesRequest]) (*connect.Response[api.ListMachinesResponse], error) {
principal := CurrentPrincipal(ctx)
if !principal.IsSystemAdmin() && !principal.TailnetMatches(req.Msg.TailnetId) {
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(req.Msg.TailnetId) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
@@ -76,7 +76,7 @@ func (s *Service) DeleteMachine(ctx context.Context, req *connect.Request[api.De
return nil, connect.NewError(connect.CodeNotFound, errors.New("machine not found"))
}
if !principal.IsSystemAdmin() && !principal.UserMatches(m.UserID) {
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(m.TailnetID) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
@@ -101,7 +101,7 @@ func (s *Service) ExpireMachine(ctx context.Context, req *connect.Request[api.Ex
return nil, connect.NewError(connect.CodeNotFound, errors.New("machine not found"))
}
if !principal.IsSystemAdmin() && !principal.UserMatches(m.UserID) {
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(m.TailnetID) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
@@ -130,7 +130,7 @@ func (s *Service) GetMachineRoutes(ctx context.Context, req *connect.Request[api
return nil, connect.NewError(connect.CodeNotFound, errors.New("machine not found"))
}
if !principal.IsSystemAdmin() && !principal.TailnetMatches(m.TailnetID) {
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(m.TailnetID) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
@@ -161,7 +161,7 @@ func (s *Service) SetMachineRoutes(ctx context.Context, req *connect.Request[api
return nil, connect.NewError(connect.CodeNotFound, errors.New("machine not found"))
}
if !principal.IsSystemAdmin() {
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(m.TailnetID) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
@@ -208,7 +208,7 @@ func (s *Service) SetMachineKeyExpiry(ctx context.Context, req *connect.Request[
return nil, connect.NewError(connect.CodeNotFound, errors.New("machine not found"))
}
if !principal.IsSystemAdmin() {
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(m.TailnetID) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
+2 -2
View File
@@ -34,7 +34,7 @@ func (s *Service) CreateTailnet(ctx context.Context, req *connect.Request[api.Cr
func (s *Service) GetTailnet(ctx context.Context, req *connect.Request[api.GetTailnetRequest]) (*connect.Response[api.GetTailnetResponse], error) {
principal := CurrentPrincipal(ctx)
if !principal.IsSystemAdmin() && !principal.TailnetMatches(req.Msg.Id) {
if !principal.IsSystemAdmin() && !principal.IsTailnetAdmin(req.Msg.Id) {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}
@@ -83,7 +83,7 @@ func (s *Service) ListTailnets(ctx context.Context, req *connect.Request[api.Lis
func (s *Service) DeleteTailnet(ctx context.Context, req *connect.Request[api.DeleteTailnetRequest]) (*connect.Response[api.DeleteTailnetResponse], error) {
principal := CurrentPrincipal(ctx)
if !principal.IsSystemAdmin() && !principal.TailnetMatches(req.Msg.TailnetId) {
if !principal.IsSystemAdmin() {
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("permission denied"))
}