mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-23 13:07:54 +00:00
fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! feat(permissions): Addeding system user support for permission check v2
This commit is contained in:
3
Makefile
3
Makefile
@@ -113,7 +113,8 @@ core_unit_test:
|
||||
|
||||
.PHONY: core_integration_db_up
|
||||
core_integration_db_up:
|
||||
docker compose -f internal/integration/config/docker-compose.yaml up --pull always --wait $${INTEGRATION_DB_FLAVOR} cache
|
||||
# docker compose -f internal/integration/config/docker-compose.yaml up --pull always --wait $${INTEGRATION_DB_FLAVOR} cache
|
||||
docker compose -f internal/integration/config/docker-compose.yaml up --wait $${INTEGRATION_DB_FLAVOR} cache
|
||||
|
||||
.PHONY: core_integration_db_down
|
||||
core_integration_db_down:
|
||||
|
@@ -4,7 +4,11 @@ CREATE OR REPLACE FUNCTION eventstore.permitted_orgs(
|
||||
instanceId TEXT
|
||||
, userId TEXT
|
||||
, perm TEXT
|
||||
, system_user_perms TEXT[]
|
||||
, system_user_memeber_type INTEGER[]
|
||||
, system_user_instance_id TEXT[]
|
||||
, system_user_aggregate_id TEXT[]
|
||||
, system_user_permissions TEXT[][]
|
||||
, system_user_permissions_length TEXT[][]
|
||||
, filter_orgs TEXT
|
||||
|
||||
, org_ids OUT TEXT[]
|
||||
@@ -16,12 +20,12 @@ DECLARE
|
||||
matched_roles TEXT[]; -- roles containing permission
|
||||
BEGIN
|
||||
|
||||
IF system_user_perms IS NOT NULL THEN
|
||||
IF system_user_memeber_type IS NOT NULL THEN
|
||||
DECLARE
|
||||
system_user_permission_found bool;
|
||||
BEGIN
|
||||
SELECT result.perm_found INTO system_user_permission_found
|
||||
FROM (SELECT COALESCE((SELECT array_position(system_user_perms, perm) > 0 ), false) AS perm_found) AS result;
|
||||
FROM (SELECT eventstore.get_org_permission(perm, instanceId,filter_orgs, system_user_memeber_type, system_user_instance_id, system_user_aggregate_id, system_user_permissions, system_user_permissions_length) AS perm_found) AS result;
|
||||
|
||||
IF system_user_permission_found THEN
|
||||
SELECT array_agg(o.org_id) INTO org_ids
|
||||
@@ -75,3 +79,83 @@ BEGIN
|
||||
RETURN;
|
||||
END;
|
||||
$$;
|
||||
|
||||
DROP FUNCTION IF EXISTS eventstore.get_org_permission;
|
||||
CREATE OR REPLACE FUNCTION eventstore.get_org_permission(
|
||||
perm TEXT
|
||||
, istance_id TEXT
|
||||
, org_id TEXT
|
||||
, system_user_memeber_type INTEGER[]
|
||||
, sustem_user_instance_id TEXT[]
|
||||
, system_user_aggregate_id TEXT[]
|
||||
, system_user_permissions TEXT[][]
|
||||
, system_user_permissions_length TEXT[][]
|
||||
-- , outt OUT TEXT[]
|
||||
, outt OUT BOOL
|
||||
)
|
||||
LANGUAGE 'plpgsql'
|
||||
AS $$
|
||||
DECLARE
|
||||
i INTEGER;
|
||||
length INTEGER;
|
||||
permission_length INTEGER;
|
||||
BEGIN
|
||||
outt := FALSE;
|
||||
length := array_length(system_user_memeber_type, 1);
|
||||
-- length := 3;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS permissions;
|
||||
CREATE TEMPORARY TABLE permissions (
|
||||
member_type INTEGER,
|
||||
instance_id TEXT,
|
||||
aggregate_id TEXT,
|
||||
permission TEXT
|
||||
);
|
||||
|
||||
-- <<outer_loop>>
|
||||
FOR i IN 1..length LOOP
|
||||
-- only interested in organization level and higher
|
||||
IF system_user_memeber_type[i] > 3 THEN
|
||||
CONTINUE;
|
||||
END IF;
|
||||
permission_length := system_user_permissions_length[i];
|
||||
|
||||
FOR j IN 1..permission_length LOOP
|
||||
IF system_user_permissions[i][j] != perm THEN
|
||||
CONTINUE;
|
||||
END IF;
|
||||
INSERT INTO permissions (member_type, instance_id, aggregate_id, permission) VALUES
|
||||
(system_user_memeber_type[i], sustem_user_instance_id[i], system_user_aggregate_id[i], system_user_permissions[i][j] );
|
||||
END LOOP;
|
||||
END LOOP;
|
||||
|
||||
outt := 4;
|
||||
RETURN;
|
||||
|
||||
SELECT TRUE INTO outt
|
||||
FROM (SELECT p.member_type FROM permissions p
|
||||
WHERE
|
||||
-- check instance id
|
||||
CASE WHEN p.member_type = 1 OR p.member_type = 2 THEN -- System or IAM
|
||||
p.aggregate_id = instance_id
|
||||
OR p.instance_id IS NULL
|
||||
ELSE
|
||||
p.instance_id = instance_id
|
||||
OR p.instance_id IS NULL
|
||||
END
|
||||
AND
|
||||
-- check organization
|
||||
CASE WHEN p.member_type = 3 THEN -- organization
|
||||
p.aggregate_id = org_id
|
||||
ELSE
|
||||
TRUE
|
||||
END
|
||||
Limit 1
|
||||
) AS result;
|
||||
|
||||
DROP TABLE permissions;
|
||||
|
||||
END;
|
||||
$$;
|
||||
|
||||
|
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
@@ -129,46 +130,69 @@ func GetAllPermissionCtxIDs(perms []string) []string {
|
||||
return ctxIDs
|
||||
}
|
||||
|
||||
type SystemUserAuthParams struct {
|
||||
MemberType []int32
|
||||
InstanceID []string
|
||||
AggregateID []string
|
||||
Permissions [][]string
|
||||
PermissionsLength []int32
|
||||
}
|
||||
|
||||
func addGetSystemUserRolesFuncToCtx(ctx context.Context, systemUserRoleMap []RoleMapping, requestedPermissions []string, ctxData CtxData) context.Context {
|
||||
if len(ctxData.SystemMemberships) == 0 {
|
||||
return ctx
|
||||
} else if ctxData.SystemMemberships[0].MemberType == MemberTypeSystem {
|
||||
ctx = context.WithValue(ctx, systemUserRolesFuncKey, func() func(ctx context.Context) ([]string, error) {
|
||||
var permissions []string
|
||||
return func(ctx context.Context) ([]string, error) {
|
||||
if permissions != nil {
|
||||
return permissions, nil
|
||||
// } else if ctxData.SystemMemberships[0].MemberType == MemberTypeSystem {
|
||||
} else {
|
||||
ctx = context.WithValue(ctx, systemUserRolesFuncKey, func() func(ctx context.Context) *SystemUserAuthParams {
|
||||
var systemUserAuthParams *SystemUserAuthParams
|
||||
chann := make(chan struct{}, 1)
|
||||
return func(ctx context.Context) *SystemUserAuthParams {
|
||||
chann <- struct{}{}
|
||||
if systemUserAuthParams != nil {
|
||||
return systemUserAuthParams
|
||||
}
|
||||
var err error
|
||||
permissions, err = getSystemUserPermissions(ctx, systemUserRoleMap)
|
||||
return permissions, err
|
||||
defer func() {
|
||||
<-chann
|
||||
close(chann)
|
||||
}()
|
||||
|
||||
systemUserAuthParams = &SystemUserAuthParams{
|
||||
MemberType: make([]int32, len(ctxData.SystemMemberships)),
|
||||
InstanceID: make([]string, len(ctxData.SystemMemberships)),
|
||||
AggregateID: make([]string, len(ctxData.SystemMemberships)),
|
||||
Permissions: make([][]string, len(ctxData.SystemMemberships)),
|
||||
PermissionsLength: make([]int32, len(ctxData.SystemMemberships)),
|
||||
}
|
||||
|
||||
for i, systemPerm := range ctxData.SystemMemberships {
|
||||
permissions := []string{}
|
||||
for _, role := range systemPerm.Roles {
|
||||
permissions = append(permissions, getPermissionsFromRole(systemUserRoleMap, role)...)
|
||||
}
|
||||
slices.Sort(permissions)
|
||||
permissions = slices.Compact(permissions)
|
||||
|
||||
systemUserAuthParams.MemberType[i] = MemberTypeServerToMemberTypeDBMap[systemPerm.MemberType]
|
||||
systemUserAuthParams.InstanceID[i] = systemPerm.InstanceID
|
||||
systemUserAuthParams.AggregateID[i] = systemPerm.AggregateID
|
||||
systemUserAuthParams.Permissions[i] = permissions
|
||||
systemUserAuthParams.PermissionsLength[i] = int32(len(permissions))
|
||||
}
|
||||
return systemUserAuthParams
|
||||
}
|
||||
}())
|
||||
} else {
|
||||
ctx = context.WithValue(ctx, systemUserRolesFuncKey, func(ctx context.Context) ([]string, error) {
|
||||
return requestedPermissions, nil
|
||||
})
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
func GetSystemUserRoles(ctx context.Context) ([]string, error) {
|
||||
func GetSystemUserAuthParams(ctx context.Context) (*SystemUserAuthParams, error) {
|
||||
getSystemUserRolesFuncValue := ctx.Value(systemUserRolesFuncKey)
|
||||
if getSystemUserRolesFuncValue == nil {
|
||||
return nil, nil
|
||||
return &SystemUserAuthParams{}, nil
|
||||
}
|
||||
getSystemUserRolesFunc, ok := getSystemUserRolesFuncValue.(func(context.Context) ([]string, error))
|
||||
getSystemUserRolesFunc, ok := getSystemUserRolesFuncValue.(func(context.Context) *SystemUserAuthParams)
|
||||
if !ok {
|
||||
return nil, errors.New("unable to obtain systems role func")
|
||||
}
|
||||
return getSystemUserRolesFunc(ctx)
|
||||
}
|
||||
|
||||
func getSystemUserPermissions(ctx context.Context, systemUserRoleMap []RoleMapping) ([]string, error) {
|
||||
var permissions []string
|
||||
for _, systemUserRoles := range systemUserRoleMap {
|
||||
permissions = append(permissions, systemUserRoles.Permissions...)
|
||||
}
|
||||
|
||||
return permissions, nil
|
||||
return getSystemUserRolesFunc(ctx), nil
|
||||
}
|
||||
|
@@ -51,6 +51,7 @@ type Memberships []*Membership
|
||||
type Membership struct {
|
||||
MemberType MemberType
|
||||
AggregateID string
|
||||
InstanceID string
|
||||
// ObjectID differs from aggregate id if object is sub of an aggregate
|
||||
ObjectID string
|
||||
|
||||
@@ -71,6 +72,12 @@ const (
|
||||
MemberTypeSystem
|
||||
)
|
||||
|
||||
var MemberTypeServerToMemberTypeDBMap map[MemberType]int32 = map[MemberType]int32{
|
||||
MemberTypeSystem: 1,
|
||||
MemberTypeIAM: 2,
|
||||
MemberTypeOrganization: 3,
|
||||
}
|
||||
|
||||
type TokenVerifier interface {
|
||||
ExistsOrg(ctx context.Context, id, domain string) (string, error)
|
||||
ProjectIDAndOriginsByClientID(ctx context.Context, clientID string) (projectID string, origins []string, err error)
|
||||
|
@@ -4,11 +4,13 @@ import (
|
||||
"context"
|
||||
"crypto/rsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-jose/go-jose/v4"
|
||||
"github.com/zitadel/logging"
|
||||
"github.com/zitadel/oidc/v3/pkg/op"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
@@ -31,7 +33,14 @@ func StartSystemTokenVerifierFromConfig(issuer string, keys map[string]*SystemAP
|
||||
}
|
||||
for _, membership := range key.Memberships {
|
||||
switch membership.MemberType {
|
||||
case MemberTypeSystem, MemberTypeIAM, MemberTypeOrganization:
|
||||
case MemberTypeSystem, MemberTypeIAM:
|
||||
systemUsers[userID] = key.Memberships
|
||||
case MemberTypeOrganization:
|
||||
if membership.AggregateID != "" && membership.InstanceID == "" {
|
||||
errorMsg := fmt.Sprintf("system user %s must include InstancID if AggregateID is set for member type Organization", userID)
|
||||
logging.Error(errorMsg)
|
||||
return nil, errors.New(errorMsg)
|
||||
}
|
||||
systemUsers[userID] = key.Memberships
|
||||
case MemberTypeUnspecified, MemberTypeProject, MemberTypeProjectGrant:
|
||||
return nil, errors.New("for system users, only the membership types System, IAM and Organization are supported")
|
||||
@@ -67,7 +76,7 @@ func (s *SystemTokenVerifierFromConfig) VerifySystemToken(ctx context.Context, t
|
||||
for _, membership := range systemUserMemberships {
|
||||
if membership.MemberType == MemberTypeSystem ||
|
||||
membership.MemberType == MemberTypeIAM && GetInstance(ctx).InstanceID() == membership.AggregateID ||
|
||||
membership.MemberType == MemberTypeOrganization && orgID == membership.AggregateID {
|
||||
membership.MemberType == MemberTypeOrganization && orgID == membership.AggregateID && GetInstance(ctx).InstanceID() == membership.InstanceID {
|
||||
matchingMemberships = append(matchingMemberships, membership)
|
||||
}
|
||||
}
|
||||
|
@@ -10,7 +10,6 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/drone/envsubst"
|
||||
@@ -73,7 +72,7 @@ func privateIPv4() (net.IP, error) {
|
||||
}
|
||||
}
|
||||
|
||||
//change: use "POD_IP"
|
||||
// change: use "POD_IP"
|
||||
ip := net.ParseIP(os.Getenv("POD_IP"))
|
||||
if ip == nil {
|
||||
return nil, errors.New("no private ip address")
|
||||
@@ -139,8 +138,8 @@ func machineID() (uint16, error) {
|
||||
errors = append(errors, "No machine identification method enabled.")
|
||||
}
|
||||
|
||||
logging.WithFields("errors", strings.Join(errors, ", ")).Panic("none of the enabled methods for identifying the machine succeeded")
|
||||
//this return will never happen because of panic one line before
|
||||
// logging.WithFields("errors", strings.Join(errors, ", ")).Panic("none of the enabled methods for identifying the machine succeeded")
|
||||
// this return will never happen because of panic one line before
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
|
@@ -88,6 +88,13 @@ SystemAPIUsers:
|
||||
- MemberType: IAM
|
||||
Roles:
|
||||
- "NO_ROLES"
|
||||
- MemberType: Organization
|
||||
Roles:
|
||||
- "SYSTEM_OWNER"
|
||||
- "IAM_OWNER"
|
||||
- "ORG_OWNER"
|
||||
AggregateID: "123456789012345678"
|
||||
InstanceID: "123456789012345678"
|
||||
KeyData: "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFqTVdFWDZtK0gzWndaV1ptTUhxbApHbVoxa0wvRVlWZzJCb24yQm5wOU5LTXdoVTlpK29CcUwrR0FzVVZYdnBkMmhVTy9ZK1VpVzlRdnJ4K3ZBeVpNCmdrNjRRNlFFNm5ZMWJncnV3aEJDUC85ZWlMMzVvOTRHelhiS2RDSEF5bFNBQmRHemZaTDN1YUgwVndvRk9neU0KZkJveTdGMHFLRXA0bVp5ZUhmMFo3ZXZacVVyRDVNcEZMTjBhUnRqVWpwOTFpd0tGU29kYXY1S25sYW4vSGtQaQpzN3NnLzBmVURRRDRzZ2ZvcndManJWYnI1aUtxSTBHQ3VhUEwzazRQOEdnY1haczVJcHUzb1BDZXdWUTBvd1hoCjJvRXVTdlNDYS8wTmxYanRLMlRqbmlYeTVSL2NaVXF3NzNOd0NFdjl4N1pLaU51dkpEWkw2UnM5Q0xJT3RhVkUKTFFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg=="
|
||||
|
||||
InitProjections:
|
||||
|
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
const (
|
||||
// eventstore.permitted_orgs(instanceid text, userid text, perm text, system_user_perms text[], filter_orgs text)
|
||||
wherePermittedOrgsClause = "%s = ANY(eventstore.permitted_orgs(?, ?, ?, ?, ?))"
|
||||
wherePermittedOrgsClause = "%s = ANY(eventstore.permitted_orgs(?, ?, ?, ?, ?, ?, ?, ?, ?))"
|
||||
wherePermittedOrgsOrCurrentUserClause = "(" + wherePermittedOrgsClause + " OR %s = ?" + ")"
|
||||
)
|
||||
|
||||
@@ -24,7 +24,7 @@ const (
|
||||
// and is typically the `resource_owner` column in ZITADEL.
|
||||
// We use full identifiers in the query builder so this function should be
|
||||
// called with something like `UserResourceOwnerCol.identifier()` for example.
|
||||
func wherePermittedOrgs(ctx context.Context, query sq.SelectBuilder, systemUserPermissions []string, filterOrgIds, orgIDColumn, permission string) sq.SelectBuilder {
|
||||
func wherePermittedOrgs(ctx context.Context, query sq.SelectBuilder, systemUserAuthParams *authz.SystemUserAuthParams, filterOrgIds, orgIDColumn, permission string) sq.SelectBuilder {
|
||||
userID := authz.GetCtxData(ctx).UserID
|
||||
logging.WithFields("permission_check_v2_flag", authz.GetFeatures(ctx).PermissionCheckV2, "org_id_column", orgIDColumn, "permission", permission, "user_id", userID).Debug("permitted orgs check used")
|
||||
|
||||
@@ -33,12 +33,12 @@ func wherePermittedOrgs(ctx context.Context, query sq.SelectBuilder, systemUserP
|
||||
authz.GetInstance(ctx).InstanceID(),
|
||||
userID,
|
||||
permission,
|
||||
systemUserPermissions,
|
||||
systemUserAuthParams,
|
||||
filterOrgIds,
|
||||
)
|
||||
}
|
||||
|
||||
func wherePermittedOrgsOrCurrentUser(ctx context.Context, query sq.SelectBuilder, systemUserPermissions []string, filterOrgIds, orgIDColumn, userIdColum, permission string) sq.SelectBuilder {
|
||||
func wherePermittedOrgsOrCurrentUser(ctx context.Context, query sq.SelectBuilder, systemUserAuthParams *authz.SystemUserAuthParams, filterOrgIds, orgIDColumn, userIdColum, permission string) sq.SelectBuilder {
|
||||
userID := authz.GetCtxData(ctx).UserID
|
||||
logging.WithFields("permission_check_v2_flag", authz.GetFeatures(ctx).PermissionCheckV2, "org_id_column", orgIDColumn, "user_id_colum", userIdColum, "permission", permission, "user_id", userID).Debug("permitted orgs check used")
|
||||
|
||||
@@ -47,7 +47,11 @@ func wherePermittedOrgsOrCurrentUser(ctx context.Context, query sq.SelectBuilder
|
||||
authz.GetInstance(ctx).InstanceID(),
|
||||
userID,
|
||||
permission,
|
||||
systemUserPermissions,
|
||||
systemUserAuthParams.MemberType,
|
||||
systemUserAuthParams.InstanceID,
|
||||
systemUserAuthParams.AggregateID,
|
||||
systemUserAuthParams.Permissions,
|
||||
systemUserAuthParams.PermissionsLength,
|
||||
filterOrgIds,
|
||||
userID,
|
||||
)
|
||||
|
@@ -5,6 +5,7 @@ import (
|
||||
"database/sql"
|
||||
_ "embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -630,6 +631,7 @@ func (q *Queries) CountUsers(ctx context.Context, queries *UserSearchQueries) (c
|
||||
return err
|
||||
}, stmt, args...)
|
||||
if err != nil {
|
||||
fmt.Printf("@@ >>>>>>>>>>>>>>>>>>>>>>>>>>>> err = %+v\n", err)
|
||||
return 0, zerrors.ThrowInternal(err, "QUERY-AG4gs", "Errors.Internal")
|
||||
}
|
||||
return count, err
|
||||
@@ -656,7 +658,7 @@ func (q *Queries) searchUsers(ctx context.Context, queries *UserSearchQueries, f
|
||||
})
|
||||
if permissionCheckV2 {
|
||||
// extract system user roles
|
||||
systemUserPermissions, err := authz.GetSystemUserRoles(ctx)
|
||||
systemUserPermissions, err := authz.GetSystemUserAuthParams(ctx)
|
||||
if err != nil {
|
||||
return nil, zerrors.ThrowInternal(err, "QUERY-GS9gs", "Errors.Internal")
|
||||
}
|
||||
@@ -673,6 +675,7 @@ func (q *Queries) searchUsers(ctx context.Context, queries *UserSearchQueries, f
|
||||
return err
|
||||
}, stmt, args...)
|
||||
if err != nil {
|
||||
fmt.Printf("@@ >>>>>>>>>>>>>>>>>>>>>>>>>>>> err = %+v\n", err)
|
||||
return nil, zerrors.ThrowInternal(err, "QUERY-AG4gs", "Errors.Internal")
|
||||
}
|
||||
users.State, err = q.latestState(ctx, userTable)
|
||||
|
Reference in New Issue
Block a user