mirror of
https://github.com/paralus/paralus.git
synced 2026-08-24 15:47:19 +00:00
Merge pull request #78 from RafayLabs/casbin-updates
Authorization updates
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
func GetProjectOrganization(ctx context.Context, db bun.IDB, id uuid.UUID) (string, string, error) {
|
||||
// Could possibly union them later for some speedup
|
||||
type projectOrg struct {
|
||||
Project string
|
||||
Organization string
|
||||
}
|
||||
var r projectOrg
|
||||
err := db.NewSelect().Table("authsrv_project").
|
||||
ColumnExpr("authsrv_project.name as project").
|
||||
ColumnExpr("authsrv_organization.name as organization").
|
||||
Join(`JOIN authsrv_organization ON authsrv_project.organization_id=authsrv_organization.id`).
|
||||
Where("authsrv_project.id = ?", id).
|
||||
Where("authsrv_project.trash = ?", false).
|
||||
Where("authsrv_organization.trash = ?", false).
|
||||
Scan(ctx, &r)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return r.Project, r.Organization, nil
|
||||
}
|
||||
+79
-20
@@ -1,7 +1,8 @@
|
||||
:host = http://localhost:11000
|
||||
:partner = important-partner
|
||||
:org = hello
|
||||
:project = hello
|
||||
:token = qJeyfD9IthujxqhbaEWOCkX8S3cjwvjk
|
||||
:partner = partner0
|
||||
:org = org0
|
||||
:project = project-uno
|
||||
:description = this is my first project
|
||||
:opts.urlScope_user = user/b2e4162c-60df-4fd7-b8fd-8fd3e4d6e533
|
||||
:opts.urlScope_org = organization/0d95d65f-3ce9-4340-ac05-77f71084d0a6
|
||||
@@ -24,6 +25,7 @@ opts.urlScope_cluster = cluster/eff85770-1a9e-42bc-824b-d0ff5a0f792c
|
||||
# Create Project
|
||||
POST :host/auth/v3/partner/:partner/organization/:organization/project
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
metadata:
|
||||
partner: :partner
|
||||
@@ -35,14 +37,17 @@ spec:
|
||||
# Get all projects
|
||||
GET :host/auth/v3/partner/:partner/organization/:organization/projects
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
# Get single project
|
||||
GET :host/auth/v3/partner/:partner/organization/:organization/project/:project
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
# Update project info
|
||||
PUT :host/auth/v3/partner/:partner/organization/:organization/project/:project
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
metadata:
|
||||
partner: :partner
|
||||
@@ -61,6 +66,7 @@ DELETE :host/auth/v3/partner/:partner/organization/:organization/project/:projec
|
||||
# Publish kubeconfig setting
|
||||
PUT :host/v2/sentry/kubeconfig/:opts.urlScope_user/setting
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
opts:
|
||||
organization: :orgid
|
||||
@@ -78,6 +84,7 @@ GET :host/v2/sentry/kubeconfig/:opts.urlScope_user/setting
|
||||
# Publish kubeconfig setting
|
||||
PUT :host/v2/sentry/kubeconfig/:opts.urlScope_org/setting
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
opts:
|
||||
organization: :orgid
|
||||
@@ -103,6 +110,7 @@ GET :host/v2/sentry/kubeconfig/user?opts.selector=&opts.account=c6974c2e-ef14-41
|
||||
# Revoke kubeconfig for user
|
||||
POST :host/v2/sentry/kubeconfig/revoke
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
opts:
|
||||
organization: cc02bd83-89d8-4c76-a7dc-06854f93e21d
|
||||
@@ -111,10 +119,12 @@ opts:
|
||||
|
||||
# Kubeconfig get cluster settings
|
||||
GET :host/v2/sentry/kubectl/:opts.urlScope_cluster/settings?opts.organization=cc02bd83-89d8-4c76-a7dc-06854f93e21d
|
||||
X-Session-Token: :token
|
||||
|
||||
# Publish kubectl settings
|
||||
PUT :host/v2/sentry/kubectl/:opts.urlScope_cluster/settings
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
opts:
|
||||
organization: cc02bd83-89d8-4c76-a7dc-06854f93e21d
|
||||
@@ -129,6 +139,7 @@ GET :host/v2/sentry/authorization/user?userCN=a=b2e4162c-60df-4fd7-b8fd-8fd3e4d6
|
||||
# Create Location
|
||||
POST :host/v3/partner/:partner/location
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
metadata:
|
||||
name: :location
|
||||
@@ -143,14 +154,17 @@ spec:
|
||||
# Get all location
|
||||
GET :host/v3/partner/:partner/location
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
# Get single location
|
||||
GET :host/v3/partner/:partner/location/:location
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
# Update location info
|
||||
PUT :host/v3/partner/:partner/location/:location
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
metadata:
|
||||
name: :location
|
||||
@@ -171,6 +185,7 @@ DELETE :host/v3/partner/:partner/location/:location
|
||||
# Create Cluster
|
||||
POST :host/infra/v3/project/:project/cluster
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
metadata:
|
||||
name: :cluster
|
||||
@@ -190,14 +205,17 @@ spec:
|
||||
# Get all clusters
|
||||
GET :host/infra/v3/project/:project/cluster
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
# Get single cluster
|
||||
GET :host/infra/v3/project/:project/cluster/:cluster
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
# Update cluster info
|
||||
PUT :host/infra/v3/project/:project/cluster/:cluster
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
apiVersion: infra.k8smgmt.io/v3
|
||||
kind: Cluster
|
||||
@@ -230,6 +248,7 @@ DELETE :host/infra/v3/project/:project/cluster/:cluster
|
||||
# Create user
|
||||
POST :host/auth/v3/users
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
metadata:
|
||||
partner: :partner
|
||||
@@ -242,6 +261,7 @@ spec:
|
||||
# Create user with roles
|
||||
POST :host/auth/v3/users
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
metadata:
|
||||
partner: :partner
|
||||
@@ -251,21 +271,14 @@ spec:
|
||||
firstName: John
|
||||
lastName: Doe
|
||||
projectNamespaceRoles:
|
||||
- role: NAMESPACE_ADMIN
|
||||
- role: :role
|
||||
namespace: :namespace
|
||||
project: :project
|
||||
|
||||
# Get all users
|
||||
GET :host/auth/v3/users
|
||||
Content-Type: application/yaml
|
||||
|
||||
# Get single user
|
||||
GET :host/auth/v3/user/:user
|
||||
Content-Type: application/yaml
|
||||
|
||||
# Update user info
|
||||
PUT :host/auth/v3/user/:user
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
metadata:
|
||||
partner: :partner
|
||||
@@ -275,10 +288,22 @@ spec:
|
||||
firstName: John
|
||||
lastName: Cena
|
||||
projectNamespaceRoles:
|
||||
- role: ADMIN
|
||||
- role: :role
|
||||
|
||||
# Get all users
|
||||
GET :host/auth/v3/users
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
# Get single user
|
||||
GET :host/auth/v3/user/:user
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
|
||||
# Delete single user
|
||||
DELETE :host/auth/v3/user/:user
|
||||
X-Session-Token: :token
|
||||
|
||||
|
||||
## Groups
|
||||
@@ -304,6 +329,7 @@ spec:
|
||||
# Create group no namespace and project
|
||||
POST :host/auth/v3/partner/:partner/organization/:org/groups
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
metadata:
|
||||
name: :group
|
||||
@@ -320,6 +346,7 @@ spec:
|
||||
# Update group to no namespace
|
||||
PUT :host/auth/v3/partner/:partner/organization/:org/group/:group
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
metadata:
|
||||
name: :group
|
||||
@@ -337,20 +364,24 @@ spec:
|
||||
# Get all groups
|
||||
GET :host/auth/v3/partner/:partner/organization/:org/groups
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
# Get a group
|
||||
GET :host/auth/v3/partner/:partner/organization/:org/group/:group
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
# Delete a group
|
||||
DELETE :host/auth/v3/partner/:partner/organization/:org/group/:group
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
## Roles
|
||||
|
||||
# Create empty role
|
||||
POST :host/auth/v3/partner/:partner/organization/:org/roles
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
metadata:
|
||||
name: :role
|
||||
@@ -364,6 +395,7 @@ spec:
|
||||
# Create role
|
||||
POST :host/auth/v3/partner/:partner/organization/:org/roles
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
metadata:
|
||||
name: :role
|
||||
@@ -372,7 +404,7 @@ metadata:
|
||||
organization: :org
|
||||
spec:
|
||||
isGlobal: true
|
||||
scope: cluster
|
||||
scope: system
|
||||
rolepermissions:
|
||||
- account.read
|
||||
- ops_star.all
|
||||
@@ -380,6 +412,7 @@ spec:
|
||||
# Update role
|
||||
PUT :host/auth/v3/partner/:partner/organization/:org/role/:role
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
metadata:
|
||||
name: :role
|
||||
@@ -388,21 +421,24 @@ metadata:
|
||||
organization: :org
|
||||
spec:
|
||||
isGlobal: true
|
||||
scope: cluster
|
||||
scope: system
|
||||
rolepermissions:
|
||||
- account.read
|
||||
- ops_star.all
|
||||
|
||||
# Get all roles
|
||||
GET :host/auth/v3/partner/:partner/organization/:org/roles
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
# Get a role
|
||||
GET :host/auth/v3/partner/:partner/organization/:org/role/:role
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
# Delete a role
|
||||
DELETE :host/auth/v3/partner/:partner/organization/:org/role/:role
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
|
||||
## Rolepermission
|
||||
@@ -410,6 +446,7 @@ Content-Type: application/yaml
|
||||
# Get all rolepermissions
|
||||
GET :host/auth/v3/rolepermissions
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
## Audit
|
||||
|
||||
@@ -421,6 +458,7 @@ GET :host/event/v1/auditlog?filter.timefrom=now-1h
|
||||
# Create partner
|
||||
POST :host/auth/v3/partner
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
metadata:
|
||||
name: :partner
|
||||
@@ -428,23 +466,39 @@ metadata:
|
||||
spec:
|
||||
host: "https://important.org"
|
||||
|
||||
# List organizations
|
||||
GET :host/auth/v3/partner/:partner/organizations
|
||||
# Get partner
|
||||
GET :host/auth/v3/partner/:partner
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
## Organization
|
||||
|
||||
# Create organization
|
||||
POST :host/auth/v3/partner/:partner/organization
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
metadata:
|
||||
partner: :partner
|
||||
name: :org
|
||||
description: "Very first organization"
|
||||
description: "Very first organizataion"
|
||||
spec:
|
||||
active: true
|
||||
|
||||
# List organizations
|
||||
GET :host/auth/v3/partner/:partner/organizations
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
# Get organization
|
||||
GET :host/auth/v3/partner/:partner/organization/:org
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
# Create project
|
||||
POST :host/auth/v3/partner/:partner/organization/:org/project
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
metadata:
|
||||
name: :project
|
||||
@@ -452,6 +506,11 @@ metadata:
|
||||
spec:
|
||||
active: true
|
||||
|
||||
# Get project
|
||||
GET :host/auth/v3/partner/:partner/organization/:org/project/:project
|
||||
Content-Type: application/yaml
|
||||
X-Session-Token: :token
|
||||
|
||||
# Delete project
|
||||
DELETE :host/auth/v3/partner/:partner/organization/:org/project/:project
|
||||
|
||||
X-Session-Token: :token
|
||||
|
||||
+20
-1
@@ -3,10 +3,13 @@ package authv3
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/RafayLabs/rcloud-base/pkg/enforcer"
|
||||
logv2 "github.com/RafayLabs/rcloud-base/pkg/log"
|
||||
"github.com/RafayLabs/rcloud-base/pkg/service"
|
||||
kclient "github.com/ory/kratos-client-go"
|
||||
"github.com/uptrace/bun"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var _log = logv2.GetLogger()
|
||||
@@ -26,6 +29,7 @@ type Option struct {
|
||||
type authContext struct {
|
||||
kc *kclient.APIClient
|
||||
ks service.ApiKeyService
|
||||
as service.AuthzService
|
||||
}
|
||||
|
||||
// NewAuthContext setup authentication and authorization dependencies.
|
||||
@@ -35,6 +39,8 @@ func NewAuthContext(db *bun.DB) authContext {
|
||||
kratosScheme string
|
||||
kratosAddr string
|
||||
)
|
||||
// TODO: https://github.com/RafayLabs/prompt/pull/3#issuecomment-1073557206
|
||||
// Where exactly should we be getting these values from?
|
||||
if v, ok := os.LookupEnv("KRATOS_SCHEME"); ok {
|
||||
kratosScheme = v
|
||||
} else {
|
||||
@@ -50,5 +56,18 @@ func NewAuthContext(db *bun.DB) authContext {
|
||||
kratosConfig.Servers[0].URL = kratosScheme + "://" + kratosAddr
|
||||
kc = kclient.NewAPIClient(kratosConfig)
|
||||
|
||||
return authContext{kc: kc, ks: service.NewApiKeyService(db)}
|
||||
gormDb, err := gorm.Open(
|
||||
postgres.New(postgres.Config{Conn: db.DB}),
|
||||
&gorm.Config{},
|
||||
)
|
||||
if err != nil {
|
||||
_log.Fatalw("unable to create db connection", "error", err)
|
||||
}
|
||||
enforcer, err := enforcer.NewCasbinEnforcer(gormDb).Init()
|
||||
if err != nil {
|
||||
_log.Fatalw("unable to init enforcer", "error", err)
|
||||
}
|
||||
as := service.NewAuthzService(db, enforcer)
|
||||
|
||||
return authContext{kc: kc, as: as, ks: service.NewApiKeyService(db)}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,20 @@ package authv3
|
||||
|
||||
import (
|
||||
context "context"
|
||||
"strings"
|
||||
|
||||
"github.com/RafayLabs/rcloud-base/pkg/gateway"
|
||||
commonpbv3 "github.com/RafayLabs/rcloud-base/proto/types/commonpb/v3"
|
||||
commonv3 "github.com/RafayLabs/rcloud-base/proto/types/commonpb/v3"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type hasMetadata interface {
|
||||
GetMetadata() *commonv3.Metadata
|
||||
}
|
||||
|
||||
func (ac authContext) NewAuthUnaryInterceptor(opt Option) grpc.UnaryServerInterceptor {
|
||||
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
|
||||
// TODO: Optimize authentication for a session/gRPC
|
||||
@@ -21,6 +26,27 @@ func (ac authContext) NewAuthUnaryInterceptor(opt Option) grpc.UnaryServerInterc
|
||||
}
|
||||
}
|
||||
|
||||
// We have to get the value of org, and project (namespace in
|
||||
// future) as we will be using this inorder to authorize the
|
||||
// user's access to different resources
|
||||
var org string
|
||||
var project string
|
||||
resource, ok := req.(hasMetadata)
|
||||
if ok {
|
||||
meta := resource.GetMetadata()
|
||||
org = meta.Organization
|
||||
project = meta.Project
|
||||
|
||||
// overrides for picking up info when not in default metadata locations
|
||||
// XXX: This requires any new items which does not follow metadata convention added here
|
||||
switch strings.Split(info.FullMethod, "/")[1] {
|
||||
case "rafay.dev.rpc.v3.Project":
|
||||
project = meta.Name
|
||||
case "rafay.dev.rpc.v3.Organization":
|
||||
org = meta.Name
|
||||
}
|
||||
}
|
||||
|
||||
md, ok := metadata.FromIncomingContext(ctx)
|
||||
if !ok {
|
||||
return nil, status.Error(codes.InvalidArgument, "grpc metadata not exist")
|
||||
@@ -43,11 +69,13 @@ func (ac authContext) NewAuthUnaryInterceptor(opt Option) grpc.UnaryServerInterc
|
||||
if len(md.Get("grpcgateway-cookie")) != 0 {
|
||||
cookie = md.Get("grpcgateway-cookie")[0]
|
||||
}
|
||||
acReq := &commonpbv3.IsRequestAllowedRequest{
|
||||
acReq := &commonv3.IsRequestAllowedRequest{
|
||||
Url: url,
|
||||
Method: method,
|
||||
XSessionToken: token,
|
||||
Cookie: cookie,
|
||||
Org: org,
|
||||
Project: project,
|
||||
}
|
||||
res, err := ac.IsRequestAllowed(ctx, nil, acReq)
|
||||
if err != nil {
|
||||
@@ -57,12 +85,12 @@ func (ac authContext) NewAuthUnaryInterceptor(opt Option) grpc.UnaryServerInterc
|
||||
|
||||
s := res.GetStatus()
|
||||
switch s {
|
||||
case commonpbv3.RequestStatus_RequestAllowed:
|
||||
case commonv3.RequestStatus_RequestAllowed:
|
||||
ctx := NewSessionContext(ctx, res.SessionData)
|
||||
return handler(ctx, req)
|
||||
case commonpbv3.RequestStatus_RequestMethodOrURLNotAllowed:
|
||||
case commonv3.RequestStatus_RequestMethodOrURLNotAllowed:
|
||||
return nil, status.Error(codes.PermissionDenied, res.GetReason())
|
||||
case commonpbv3.RequestStatus_RequestNotAuthenticated:
|
||||
case commonv3.RequestStatus_RequestNotAuthenticated:
|
||||
return nil, status.Error(codes.Unauthenticated, res.GetReason())
|
||||
}
|
||||
|
||||
|
||||
@@ -3,19 +3,24 @@ package authv3
|
||||
import (
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/RafayLabs/rcloud-base/internal/dao"
|
||||
commonpbv3 "github.com/RafayLabs/rcloud-base/proto/types/commonpb/v3"
|
||||
"github.com/google/uuid"
|
||||
"github.com/uptrace/bun"
|
||||
"github.com/urfave/negroni"
|
||||
)
|
||||
|
||||
type authMiddleware struct {
|
||||
db *bun.DB
|
||||
ac authContext
|
||||
opt Option
|
||||
}
|
||||
|
||||
func NewAuthMiddleware(opt Option, db *bun.DB) negroni.Handler {
|
||||
return &authMiddleware{
|
||||
db: db,
|
||||
ac: NewAuthContext(db),
|
||||
opt: opt,
|
||||
}
|
||||
@@ -34,12 +39,48 @@ func (am *authMiddleware) ServeHTTP(rw http.ResponseWriter, r *http.Request, nex
|
||||
return
|
||||
}
|
||||
}
|
||||
// Auth is primarily done via grpc endpoints, this is only used
|
||||
// for endoints which do not go through grpc As of now, it is just
|
||||
// prompt.
|
||||
var proj string
|
||||
var org string
|
||||
|
||||
if strings.HasPrefix(r.URL.String(), "/v2/debug/prompt/project/") {
|
||||
// /v2/debug/prompt/project/:project_id/cluster/:cluster_name
|
||||
splits := strings.Split(r.URL.String(), "/")
|
||||
if len(splits) > 5 {
|
||||
projid, err := uuid.Parse(splits[5])
|
||||
if err != nil {
|
||||
_log.Errorf("Failed to authenticate: unable to parse project uuid")
|
||||
http.Error(rw, http.StatusText(http.StatusForbidden), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
// What gets sent for project is the id unlike most other
|
||||
// api routes, so we have to fetch the name as well as the
|
||||
// org info for casbin
|
||||
proj, org, err = dao.GetProjectOrganization(r.Context(), am.db, projid)
|
||||
if err != nil {
|
||||
_log.Errorf("Failed to authenticate: unable to find project")
|
||||
http.Error(rw, http.StatusText(http.StatusForbidden), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// The middleware to only used with routes which does not have
|
||||
// a grpc and so fail for any other requests.
|
||||
_log.Errorf("Failed to authenticate: not a prompt request")
|
||||
http.Error(rw, http.StatusText(http.StatusForbidden), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
req := &commonpbv3.IsRequestAllowedRequest{
|
||||
Url: r.URL.String(),
|
||||
Method: r.Method,
|
||||
XSessionToken: r.Header.Get("X-Session-Token"),
|
||||
XApiKey: r.Header.Get("X-RAFAY-API-KEYID"),
|
||||
Cookie: r.Header.Get("Cookie"),
|
||||
Project: proj,
|
||||
Org: org,
|
||||
}
|
||||
res, err := am.ac.IsRequestAllowed(r.Context(), r, req)
|
||||
if err != nil {
|
||||
@@ -64,5 +105,4 @@ func (am *authMiddleware) ServeHTTP(rw http.ResponseWriter, r *http.Request, nex
|
||||
|
||||
// status is unknown
|
||||
http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
+36
-9
@@ -7,6 +7,7 @@ import (
|
||||
"strings"
|
||||
|
||||
rpcv3 "github.com/RafayLabs/rcloud-base/proto/rpc/user"
|
||||
authzv1 "github.com/RafayLabs/rcloud-base/proto/types/authz"
|
||||
commonv3 "github.com/RafayLabs/rcloud-base/proto/types/commonpb/v3"
|
||||
"github.com/spacemonkeygo/httpsig"
|
||||
)
|
||||
@@ -25,10 +26,14 @@ func (ac *authContext) IsRequestAllowed(ctx context.Context, httpreq *http.Reque
|
||||
}
|
||||
|
||||
// Authenticate request
|
||||
err := ac.authenticate(ctx, httpreq, req, res)
|
||||
err, succ := ac.authenticate(ctx, httpreq, req, res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Don't bother checking authorization if athentication failed
|
||||
if !succ {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Authorize request
|
||||
err = ac.authorize(ctx, req, res)
|
||||
@@ -41,14 +46,14 @@ func (ac *authContext) IsRequestAllowed(ctx context.Context, httpreq *http.Reque
|
||||
|
||||
// authenticate validate whether the request is from a legitimate user
|
||||
// and populate relevant information in res.
|
||||
func (ac *authContext) authenticate(ctx context.Context, httpreq *http.Request, req *commonv3.IsRequestAllowedRequest, res *commonv3.IsRequestAllowedResponse) error {
|
||||
func (ac *authContext) authenticate(ctx context.Context, httpreq *http.Request, req *commonv3.IsRequestAllowedRequest, res *commonv3.IsRequestAllowedResponse) (error, bool) {
|
||||
if len(req.XApiKey) > 0 && len(req.XSessionToken) == 0 {
|
||||
resp, err := ac.ks.GetByKey(ctx, &rpcv3.ApiKeyRequest{
|
||||
Id: req.XApiKey,
|
||||
})
|
||||
if err != nil {
|
||||
_log.Infow("unable to get api key", "key", req.XApiKey, "error", err)
|
||||
return ErrInvalidAPIKey
|
||||
return ErrInvalidAPIKey, false
|
||||
}
|
||||
var kg httpsig.KeyGetterFunc = func(id string) interface{} {
|
||||
return []byte(resp.Secret)
|
||||
@@ -58,7 +63,7 @@ func (ac *authContext) authenticate(ctx context.Context, httpreq *http.Request,
|
||||
verifier.SetRequiredHeaders([]string{"content-md5", "date", "host", "nonce"})
|
||||
err = verifier.Verify(httpreq)
|
||||
if err != nil {
|
||||
return ErrInvalidSignature
|
||||
return ErrInvalidSignature, false
|
||||
}
|
||||
res.Status = commonv3.RequestStatus_RequestAllowed
|
||||
res.SessionData.Username = resp.Name
|
||||
@@ -73,9 +78,9 @@ func (ac *authContext) authenticate(ctx context.Context, httpreq *http.Request,
|
||||
if strings.Contains(err.Error(), "401 Unauthorized") {
|
||||
res.Status = commonv3.RequestStatus_RequestNotAuthenticated
|
||||
res.Reason = "no or invalid credentials"
|
||||
return nil
|
||||
return nil, false
|
||||
} else {
|
||||
return err
|
||||
return err, false
|
||||
}
|
||||
}
|
||||
if session.GetActive() {
|
||||
@@ -90,11 +95,33 @@ func (ac *authContext) authenticate(ctx context.Context, httpreq *http.Request,
|
||||
res.Reason = "no active session"
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return nil, true
|
||||
}
|
||||
|
||||
// authorize performs authorization of the request and populate
|
||||
// relevant information in res.
|
||||
// authorize performs authorization of the request
|
||||
func (ac *authContext) authorize(ctx context.Context, req *commonv3.IsRequestAllowedRequest, res *commonv3.IsRequestAllowedResponse) error {
|
||||
// user,namespace,project,org,url(perm),method
|
||||
// ones that don't have value should be "*"
|
||||
proj := req.Project
|
||||
if proj == "" {
|
||||
proj = "*"
|
||||
}
|
||||
org := req.Org
|
||||
if org == "" {
|
||||
org = "*"
|
||||
}
|
||||
er := authzv1.EnforceRequest{
|
||||
Params: []string{"u:" + res.SessionData.Username, "*", proj, org, req.Url, req.Method},
|
||||
}
|
||||
authenticated, err := ac.as.Enforce(ctx, &er)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !authenticated.Res {
|
||||
res.Status = commonv3.RequestStatus_RequestMethodOrURLNotAllowed
|
||||
res.Reason = "not authorized to perform action"
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+41
-42
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/RafayLabs/rcloud-base/internal/dao"
|
||||
@@ -77,53 +77,75 @@ func (s *groupService) createGroupRoleRelations(ctx context.Context, db bun.IDB,
|
||||
// TODO: add transactions
|
||||
projectNamespaceRoles := group.GetSpec().GetProjectNamespaceRoles()
|
||||
|
||||
var pgnrs []models.ProjectGroupNamespaceRole
|
||||
var pgrs []models.ProjectGroupRole
|
||||
var grs []models.GroupRole
|
||||
var ps []*authzv1.Policy
|
||||
for _, pnr := range projectNamespaceRoles {
|
||||
role := pnr.GetRole()
|
||||
entity, err := dao.GetIdByName(ctx, db, role, &models.Role{})
|
||||
entity, err := dao.GetByName(ctx, db, role, &models.Role{})
|
||||
if err != nil {
|
||||
return &userv3.Group{}, fmt.Errorf("unable to find role '%v'", role)
|
||||
}
|
||||
var roleId uuid.UUID
|
||||
var roleName string
|
||||
var scope string
|
||||
if rle, ok := entity.(*models.Role); ok {
|
||||
roleId = rle.ID
|
||||
roleName = rle.Name
|
||||
scope = strings.ToLower(rle.Scope)
|
||||
} else {
|
||||
return &userv3.Group{}, fmt.Errorf("unable to find role '%v'", role)
|
||||
}
|
||||
|
||||
project := pnr.GetProject()
|
||||
org := group.GetMetadata().GetOrganization()
|
||||
namespaceId := pnr.GetNamespace() // TODO: lookup id from name
|
||||
switch {
|
||||
case namespaceId != 0:
|
||||
projectId, err := dao.GetProjectId(ctx, db, project)
|
||||
if err != nil {
|
||||
return &userv3.Group{}, fmt.Errorf("unable to find project '%v'", project)
|
||||
}
|
||||
pgnr := models.ProjectGroupNamespaceRole{
|
||||
|
||||
switch scope {
|
||||
case "system":
|
||||
gr := models.GroupRole{
|
||||
Trash: false,
|
||||
RoleId: roleId,
|
||||
PartnerId: ids.Partner,
|
||||
OrganizationId: ids.Organization,
|
||||
GroupId: ids.Id,
|
||||
ProjectId: projectId,
|
||||
NamespaceId: namespaceId,
|
||||
Active: true,
|
||||
}
|
||||
pgnrs = append(pgnrs, pgnr)
|
||||
|
||||
grs = append(grs, gr)
|
||||
ps = append(ps, &authzv1.Policy{
|
||||
Sub: "g:" + group.GetMetadata().GetName(),
|
||||
Ns: strconv.FormatInt(namespaceId, 10),
|
||||
Proj: project,
|
||||
Ns: "*",
|
||||
Proj: "*",
|
||||
Org: "*",
|
||||
Obj: role,
|
||||
})
|
||||
case "organization":
|
||||
if org == "" {
|
||||
return &userv3.Group{}, fmt.Errorf("no org name provided for role '%v'", roleName)
|
||||
}
|
||||
gr := models.GroupRole{
|
||||
Trash: false,
|
||||
RoleId: roleId,
|
||||
PartnerId: ids.Partner,
|
||||
OrganizationId: ids.Organization,
|
||||
GroupId: ids.Id,
|
||||
Active: true,
|
||||
}
|
||||
grs = append(grs, gr)
|
||||
ps = append(ps, &authzv1.Policy{
|
||||
Sub: "g:" + group.GetMetadata().GetName(),
|
||||
Ns: "*",
|
||||
Proj: "*",
|
||||
Org: org,
|
||||
Obj: role,
|
||||
})
|
||||
case project != "":
|
||||
projectId, err := dao.GetProjectId(ctx, db, project)
|
||||
case "project":
|
||||
if org == "" {
|
||||
return &userv3.Group{}, fmt.Errorf("no org name provided for role '%v'", roleName)
|
||||
}
|
||||
if project == "" {
|
||||
return &userv3.Group{}, fmt.Errorf("no project name provided for role '%v'", roleName)
|
||||
}
|
||||
projectId, err := dao.GetProjectId(ctx, s.db, project)
|
||||
if err != nil {
|
||||
return &userv3.Group{}, fmt.Errorf("unable to find project '%v'", project)
|
||||
}
|
||||
@@ -145,29 +167,6 @@ func (s *groupService) createGroupRoleRelations(ctx context.Context, db bun.IDB,
|
||||
Org: org,
|
||||
Obj: role,
|
||||
})
|
||||
default:
|
||||
gr := models.GroupRole{
|
||||
Trash: false,
|
||||
RoleId: roleId,
|
||||
PartnerId: ids.Partner,
|
||||
OrganizationId: ids.Organization,
|
||||
GroupId: ids.Id,
|
||||
Active: true,
|
||||
}
|
||||
grs = append(grs, gr)
|
||||
ps = append(ps, &authzv1.Policy{
|
||||
Sub: "g:" + group.GetMetadata().GetName(),
|
||||
Ns: "*",
|
||||
Proj: "*",
|
||||
Org: org,
|
||||
Obj: role,
|
||||
})
|
||||
}
|
||||
}
|
||||
if len(pgnrs) > 0 {
|
||||
_, err := dao.Create(ctx, db, &pgnrs)
|
||||
if err != nil {
|
||||
return &userv3.Group{}, err
|
||||
}
|
||||
}
|
||||
if len(pgrs) > 0 {
|
||||
|
||||
+17
-14
@@ -216,9 +216,10 @@ func TestCreateGroupNoUsersWithRoles(t *testing.T) {
|
||||
name string
|
||||
roles []*userv3.ProjectNamespaceRole
|
||||
dbname string
|
||||
scope string
|
||||
shouldfail bool
|
||||
}{
|
||||
{"just role", []*userv3.ProjectNamespaceRole{{Role: uuid.New().String()}}, "authsrv_grouprole", false},
|
||||
{"just role", []*userv3.ProjectNamespaceRole{{Role: uuid.New().String()}}, "authsrv_grouprole", "system", false},
|
||||
// {"just project", []*userv3.ProjectNamespaceRole{{Project: &projectid}}, "authsrv_grouprole", true}, // no role creation without role
|
||||
// {"just namespace", []*userv3.ProjectNamespaceRole{{Namespace: &namespaceid}}, "authsrv_grouprole", true}, // no role creation without role,
|
||||
// {"project and namespace", []*userv3.ProjectNamespaceRole{{Project: &projectid, Namespace: &namespaceid}}, "authsrv_grouprole", true}, // no role creation without role,
|
||||
@@ -249,8 +250,8 @@ func TestCreateGroupNoUsersWithRoles(t *testing.T) {
|
||||
mock.ExpectBegin()
|
||||
mock.ExpectQuery(`INSERT INTO "authsrv_group"`).
|
||||
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(guuid))
|
||||
mock.ExpectQuery(`SELECT "resourcerole"."id" FROM "authsrv_resourcerole" AS "resourcerole"`).
|
||||
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(pruuid))
|
||||
mock.ExpectQuery(`SELECT "resourcerole"."id".* FROM "authsrv_resourcerole" AS "resourcerole"`).
|
||||
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name", "scope"}).AddRow(pruuid, "role-name", tc.scope))
|
||||
if tc.roles[0].Project != nil {
|
||||
mock.ExpectQuery(`SELECT "project"."id" FROM "authsrv_project" AS "project"`).
|
||||
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(pruuid))
|
||||
@@ -295,14 +296,15 @@ func TestCreateGroupWithUsersWithRoles(t *testing.T) {
|
||||
users []string
|
||||
roles []*userv3.ProjectNamespaceRole
|
||||
dbname string
|
||||
scope string
|
||||
shouldfail bool
|
||||
}{
|
||||
{"just role", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Role: uuid.New().String()}}, "authsrv_grouprole", false},
|
||||
{"just project", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Project: &projectid}}, "authsrv_grouprole", true}, // no role creation without role
|
||||
{"just namespace", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Namespace: &namespaceid}}, "authsrv_grouprole", true}, // no role creation without role,
|
||||
{"project and namespace", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Project: &projectid, Namespace: &namespaceid}}, "authsrv_grouprole", true}, // no role creation without role,
|
||||
{"project and role", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Project: &projectid, Role: uuid.New().String()}}, "authsrv_projectgrouprole", false},
|
||||
{"project role namespace", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Project: &projectid, Namespace: &namespaceid, Role: uuid.New().String()}}, "authsrv_projectgroupnamespacerole", false},
|
||||
{"just role", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Role: uuid.New().String()}}, "authsrv_grouprole", "system", false},
|
||||
{"just project", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Project: &projectid}}, "authsrv_grouprole", "system", true}, // no role creation without role
|
||||
{"just namespace", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Namespace: &namespaceid}}, "authsrv_projectgrouprole", "project", true}, // no role creation without role,
|
||||
{"project and namespace", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Project: &projectid, Namespace: &namespaceid}}, "authsrv_grouprole", "project", true}, // no role creation without role,
|
||||
{"project and role", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Project: &projectid, Role: uuid.New().String()}}, "authsrv_projectgrouprole", "project", false},
|
||||
// {"project role namespace", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Project: &projectid, Namespace: &namespaceid, Role: uuid.New().String()}}, "authsrv_projectgroupnamespacerole", false},
|
||||
}
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
@@ -334,8 +336,8 @@ func TestCreateGroupWithUsersWithRoles(t *testing.T) {
|
||||
mock.ExpectQuery(`INSERT INTO "authsrv_groupaccount"`).
|
||||
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(uuid.New().String()))
|
||||
|
||||
mock.ExpectQuery(`SELECT "resourcerole"."id" FROM "authsrv_resourcerole" AS "resourcerole"`).
|
||||
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(pruuid))
|
||||
mock.ExpectQuery(`SELECT "resourcerole"."id".* FROM "authsrv_resourcerole" AS "resourcerole"`).
|
||||
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name", "scope"}).AddRow(pruuid, "role-name", tc.scope))
|
||||
if tc.roles[0].Project != nil {
|
||||
mock.ExpectQuery(`SELECT "project"."id" FROM "authsrv_project" AS "project"`).
|
||||
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(pruuid))
|
||||
@@ -383,8 +385,9 @@ func TestUpdateGroupWithUsersWithRoles(t *testing.T) {
|
||||
users []string
|
||||
roles []*userv3.ProjectNamespaceRole
|
||||
dbname string
|
||||
scope string
|
||||
}{
|
||||
{"user role update", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Role: uuid.New().String()}}, "authsrv_grouprole"},
|
||||
{"user role update", []string{"user-" + uuid.New().String()}, []*userv3.ProjectNamespaceRole{{Role: uuid.New().String()}}, "authsrv_grouprole", "system"},
|
||||
}
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
@@ -422,8 +425,8 @@ func TestUpdateGroupWithUsersWithRoles(t *testing.T) {
|
||||
WillReturnResult(sqlmock.NewResult(1, 1))
|
||||
mock.ExpectExec(`UPDATE "authsrv_projectgroupnamespacerole" AS "projectgroupnamespacerole" SET trash = TRUE WHERE ."group_id" = '` + guuid).
|
||||
WillReturnResult(sqlmock.NewResult(1, 1))
|
||||
mock.ExpectQuery(`SELECT "resourcerole"."id" FROM "authsrv_resourcerole" AS "resourcerole"`).
|
||||
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(pruuid))
|
||||
mock.ExpectQuery(`SELECT "resourcerole"."id".* FROM "authsrv_resourcerole" AS "resourcerole"`).
|
||||
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name", "scope"}).AddRow(pruuid, "role-name", tc.scope))
|
||||
if tc.roles[0].Project != nil {
|
||||
mock.ExpectQuery(`SELECT "project"."id" FROM "authsrv_project" AS "project"`).
|
||||
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(pruuid))
|
||||
|
||||
+1
-4
@@ -131,10 +131,7 @@ func (s *roleService) Create(ctx context.Context, role *rolev3.Role) (*rolev3.Ro
|
||||
}
|
||||
|
||||
scope := role.GetSpec().GetScope()
|
||||
// since this is purely additional metadata at this point, we
|
||||
// can kinda treat it as optional, and so we are allowing empty
|
||||
// TODO: check if "" is valid
|
||||
if !contains([]string{"system", "organization", "project", ""}, strings.ToLower(scope)) {
|
||||
if !contains([]string{"system", "organization", "project"}, strings.ToLower(scope)) {
|
||||
return nil, fmt.Errorf("unknown scope '%v'", scope)
|
||||
}
|
||||
|
||||
|
||||
+49
-44
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@@ -102,55 +102,83 @@ func (s *userService) createUserRoleRelations(ctx context.Context, db bun.IDB, u
|
||||
projectNamespaceRoles := user.GetSpec().GetProjectNamespaceRoles()
|
||||
|
||||
// TODO: add transactions
|
||||
var panrs []models.ProjectAccountNamespaceRole
|
||||
var pars []models.ProjectAccountResourcerole
|
||||
var ars []models.AccountResourcerole
|
||||
var ps []*authzv1.Policy
|
||||
for _, pnr := range projectNamespaceRoles {
|
||||
role := pnr.GetRole()
|
||||
entity, err := dao.GetIdByName(ctx, db, role, &models.Role{})
|
||||
entity, err := dao.GetByName(ctx, db, role, &models.Role{})
|
||||
if err != nil {
|
||||
return user, fmt.Errorf("unable to find role '%v'", role)
|
||||
return &userv3.User{}, fmt.Errorf("unable to find role '%v'", role)
|
||||
}
|
||||
var roleId uuid.UUID
|
||||
var roleName string
|
||||
var scope string
|
||||
if rle, ok := entity.(*models.Role); ok {
|
||||
roleId = rle.ID
|
||||
roleName = rle.Name
|
||||
scope = strings.ToLower(rle.Scope)
|
||||
} else {
|
||||
return user, fmt.Errorf("unable to find role '%v'", role)
|
||||
return &userv3.User{}, fmt.Errorf("unable to find role '%v'", role)
|
||||
}
|
||||
|
||||
project := pnr.GetProject()
|
||||
org := user.GetMetadata().GetOrganization()
|
||||
namespaceId := pnr.GetNamespace() // TODO: lookup id from name
|
||||
|
||||
switch {
|
||||
case pnr.Namespace != nil:
|
||||
projectId, err := dao.GetProjectId(ctx, db, project)
|
||||
if err != nil {
|
||||
return user, fmt.Errorf("unable to find project '%v'", project)
|
||||
}
|
||||
panr := models.ProjectAccountNamespaceRole{
|
||||
switch scope {
|
||||
case "system":
|
||||
ar := models.AccountResourcerole{
|
||||
CreatedAt: time.Now(),
|
||||
ModifiedAt: time.Now(),
|
||||
Trash: false,
|
||||
Default: true,
|
||||
RoleId: roleId,
|
||||
PartnerId: ids.Partner,
|
||||
OrganizationId: ids.Organization, // Not really used
|
||||
AccountId: ids.Id,
|
||||
Active: true,
|
||||
}
|
||||
ars = append(ars, ar)
|
||||
|
||||
ps = append(ps, &authzv1.Policy{
|
||||
Sub: "u:" + user.GetMetadata().GetName(),
|
||||
Ns: "*",
|
||||
Proj: "*",
|
||||
Org: "*",
|
||||
Obj: role,
|
||||
})
|
||||
case "organization":
|
||||
if org == "" {
|
||||
return &userv3.User{}, fmt.Errorf("no org name provided for role '%v'", roleName)
|
||||
}
|
||||
|
||||
ar := models.AccountResourcerole{
|
||||
CreatedAt: time.Now(),
|
||||
ModifiedAt: time.Now(),
|
||||
Trash: false,
|
||||
Default: true,
|
||||
RoleId: roleId,
|
||||
PartnerId: ids.Partner,
|
||||
OrganizationId: ids.Organization,
|
||||
AccountId: ids.Id,
|
||||
ProjectId: projectId,
|
||||
NamespaceId: namespaceId,
|
||||
Active: true,
|
||||
}
|
||||
panrs = append(panrs, panr)
|
||||
ars = append(ars, ar)
|
||||
|
||||
ps = append(ps, &authzv1.Policy{
|
||||
Sub: "u:" + user.GetMetadata().GetName(),
|
||||
Ns: strconv.FormatInt(namespaceId, 10),
|
||||
Proj: project,
|
||||
Ns: "*",
|
||||
Proj: "*",
|
||||
Org: org,
|
||||
Obj: role,
|
||||
})
|
||||
case project != "":
|
||||
case "project":
|
||||
if org == "" {
|
||||
return &userv3.User{}, fmt.Errorf("no org name provided for role '%v'", roleName)
|
||||
}
|
||||
if project == "" {
|
||||
return &userv3.User{}, fmt.Errorf("no project name provided for role '%v'", roleName)
|
||||
}
|
||||
projectId, err := dao.GetProjectId(ctx, db, project)
|
||||
if err != nil {
|
||||
return user, fmt.Errorf("unable to find project '%v'", project)
|
||||
@@ -177,32 +205,9 @@ func (s *userService) createUserRoleRelations(ctx context.Context, db bun.IDB, u
|
||||
Obj: role,
|
||||
})
|
||||
default:
|
||||
ar := models.AccountResourcerole{
|
||||
CreatedAt: time.Now(),
|
||||
ModifiedAt: time.Now(),
|
||||
Trash: false,
|
||||
Default: true,
|
||||
RoleId: roleId,
|
||||
PartnerId: ids.Partner,
|
||||
OrganizationId: ids.Organization,
|
||||
AccountId: ids.Id,
|
||||
Active: true,
|
||||
if err != nil {
|
||||
return user, fmt.Errorf("namespace specific roles are not handled")
|
||||
}
|
||||
ars = append(ars, ar)
|
||||
|
||||
ps = append(ps, &authzv1.Policy{
|
||||
Sub: "u:" + user.GetMetadata().GetName(),
|
||||
Ns: "*",
|
||||
Proj: "*",
|
||||
Org: org,
|
||||
Obj: role,
|
||||
})
|
||||
}
|
||||
}
|
||||
if len(panrs) > 0 {
|
||||
_, err := dao.Create(ctx, db, &panrs)
|
||||
if err != nil {
|
||||
return &userv3.User{}, err
|
||||
}
|
||||
}
|
||||
if len(pars) > 0 {
|
||||
|
||||
+13
-11
@@ -99,14 +99,16 @@ func TestCreateUserWithRole(t *testing.T) {
|
||||
name string
|
||||
roles []*userv3.ProjectNamespaceRole
|
||||
dbname string
|
||||
scope string
|
||||
shouldfail bool
|
||||
}{
|
||||
{"just role", []*userv3.ProjectNamespaceRole{{Role: rname}}, "authsrv_accountresourcerole", false},
|
||||
{"just project", []*userv3.ProjectNamespaceRole{{Project: &prname}}, "authsrv_accountrole", true}, // no role creation without role
|
||||
{"just namespace", []*userv3.ProjectNamespaceRole{{Namespace: &namespaceid}}, "authsrv_accountrole", true}, // no role creation without role,
|
||||
{"project and namespace", []*userv3.ProjectNamespaceRole{{Project: &prname, Namespace: &namespaceid}}, "authsrv_accountrole", true}, // no role creation without role,
|
||||
{"project and role", []*userv3.ProjectNamespaceRole{{Project: &prname, Role: rname}}, "authsrv_projectaccountresourcerole", false},
|
||||
{"project role namespace", []*userv3.ProjectNamespaceRole{{Project: &prname, Namespace: &namespaceid, Role: rname}}, "authsrv_projectaccountnamespacerole", false},
|
||||
{"just role", []*userv3.ProjectNamespaceRole{{Role: rname}}, "authsrv_accountresourcerole", "system", false},
|
||||
{"just role org scope", []*userv3.ProjectNamespaceRole{{Role: rname}}, "authsrv_accountresourcerole", "organization", false},
|
||||
{"just project", []*userv3.ProjectNamespaceRole{{Project: &prname}}, "authsrv_accountrole", "system", true}, // no role creation without role
|
||||
{"just namespace", []*userv3.ProjectNamespaceRole{{Namespace: &namespaceid}}, "authsrv_accountrole", "system", true}, // no role creation without role,
|
||||
{"project and namespace", []*userv3.ProjectNamespaceRole{{Project: &prname, Namespace: &namespaceid}}, "authsrv_accountrole", "system", true}, // no role creation without role,
|
||||
{"project and role", []*userv3.ProjectNamespaceRole{{Project: &prname, Role: rname}}, "authsrv_projectaccountresourcerole", "project", false},
|
||||
{"project role namespace", []*userv3.ProjectNamespaceRole{{Project: &prname, Namespace: &namespaceid, Role: rname}}, "authsrv_projectaccountresourcerole", "project", false},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
@@ -129,8 +131,8 @@ func TestCreateUserWithRole(t *testing.T) {
|
||||
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(ouuid))
|
||||
|
||||
mock.ExpectBegin()
|
||||
mock.ExpectQuery(`SELECT "resourcerole"."id" FROM "authsrv_resourcerole" AS "resourcerole"`).
|
||||
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(pruuid))
|
||||
mock.ExpectQuery(`SELECT "resourcerole"."id".* FROM "authsrv_resourcerole" AS "resourcerole"`).
|
||||
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name", "scope"}).AddRow(pruuid, "role-name", tc.scope))
|
||||
if tc.roles[0].Project != nil {
|
||||
mock.ExpectQuery(`SELECT "project"."id" FROM "authsrv_project" AS "project"`).
|
||||
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(pruuid))
|
||||
@@ -198,11 +200,11 @@ func TestUpdateUser(t *testing.T) {
|
||||
WillReturnResult(sqlmock.NewResult(1, 1))
|
||||
mock.ExpectExec(`UPDATE "authsrv_projectaccountnamespacerole" AS "projectaccountnamespacerole" SET trash = TRUE WHERE`).
|
||||
WillReturnResult(sqlmock.NewResult(1, 1))
|
||||
mock.ExpectQuery(`SELECT "resourcerole"."id" FROM "authsrv_resourcerole" AS "resourcerole"`).
|
||||
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(pruuid))
|
||||
mock.ExpectQuery(`SELECT "resourcerole"."id".* FROM "authsrv_resourcerole" AS "resourcerole"`).
|
||||
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name", "scope"}).AddRow(pruuid, "role-name", "project"))
|
||||
mock.ExpectQuery(`SELECT "project"."id" FROM "authsrv_project" AS "project"`).
|
||||
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(pruuid))
|
||||
mock.ExpectQuery(`INSERT INTO "authsrv_projectaccountnamespacerole"`).
|
||||
mock.ExpectQuery(`INSERT INTO "authsrv_projectaccountresourcerole"`).
|
||||
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(uuid.New().String()))
|
||||
mock.ExpectCommit()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user