mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-04 23:45:07 +00:00
fix: user grants deactivation (#8634)
# Which Problems Are Solved ZITADEL's user grants deactivation mechanism did not work correctly. Deactivated user grants were still provided in token, which could lead to unauthorized access to applications and resources. Additionally, the management and auth API always returned the state as active or did not provide any information about the state. # How the Problems Are Solved - Correctly check the user grant state on active for tokens and user information (userinfo, introspection, saml attributes) - Map state in API and display in Console
This commit is contained in:
parent
4ac722d934
commit
ca1914e235
@ -154,10 +154,25 @@
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="state">
|
||||
<th mat-header-cell *matHeaderCellDef>{{ 'PROJECT.GRANT.STATE' | translate }}</th>
|
||||
<td mat-cell *matCellDef="let grant">
|
||||
<span
|
||||
class="state"
|
||||
[ngClass]="{
|
||||
active: grant.state === UserGrantState.USER_GRANT_STATE_ACTIVE,
|
||||
inactive: grant.state === UserGrantState.USER_GRANT_STATE_INACTIVE,
|
||||
}"
|
||||
>
|
||||
{{ 'USER.DATA.STATE' + grant.state | translate }}
|
||||
</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions" stickyEnd>
|
||||
<th mat-header-cell *matHeaderCellDef class="user-tr-actions"></th>
|
||||
<td mat-cell class="user-tr-actions" *matCellDef="let grant; let i = index">
|
||||
<cnsl-table-actions [hasActions]="true">
|
||||
<cnsl-table-actions [hasActions]="!context.includes('user')">
|
||||
<button
|
||||
actions
|
||||
matTooltip="{{ 'ACTIONS.REMOVE' | translate }}"
|
||||
|
@ -8,7 +8,13 @@ import { tap } from 'rxjs/operators';
|
||||
import { enterAnimations } from 'src/app/animations';
|
||||
import { UserGrant as AuthUserGrant } from 'src/app/proto/generated/zitadel/auth_pb';
|
||||
import { Role } from 'src/app/proto/generated/zitadel/project_pb';
|
||||
import { Type, UserGrant as MgmtUserGrant, UserGrantQuery, UserGrant } from 'src/app/proto/generated/zitadel/user_pb';
|
||||
import {
|
||||
Type,
|
||||
UserGrant as MgmtUserGrant,
|
||||
UserGrant,
|
||||
UserGrantQuery,
|
||||
UserGrantState,
|
||||
} from 'src/app/proto/generated/zitadel/user_pb';
|
||||
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
|
||||
import { ManagementService } from 'src/app/services/mgmt.service';
|
||||
import { ToastService } from 'src/app/services/toast.service';
|
||||
@ -66,6 +72,7 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
|
||||
public UserGrantContext: any = UserGrantContext;
|
||||
public Type: any = Type;
|
||||
public ActionKeysType: any = ActionKeysType;
|
||||
public UserGrantState: any = UserGrantState;
|
||||
@Input() public type: Type | undefined = undefined;
|
||||
|
||||
public filterOpen: boolean = false;
|
||||
@ -86,6 +93,7 @@ export class UserGrantsComponent implements OnInit, AfterViewInit {
|
||||
'type',
|
||||
'creationDate',
|
||||
'changeDate',
|
||||
'state',
|
||||
'roleNamesList',
|
||||
'actions',
|
||||
];
|
||||
|
@ -17,6 +17,7 @@
|
||||
'type',
|
||||
'creationDate',
|
||||
'changeDate',
|
||||
'state',
|
||||
'roleNamesList',
|
||||
'actions',
|
||||
]"
|
||||
|
@ -42,7 +42,16 @@
|
||||
[context]="UserGrantContext.GRANTED_PROJECT"
|
||||
[projectId]="projectId"
|
||||
[grantId]="grantId"
|
||||
[displayedColumns]="['select', 'user', 'projectId', 'creationDate', 'changeDate', 'roleNamesList', 'actions']"
|
||||
[displayedColumns]="[
|
||||
'select',
|
||||
'user',
|
||||
'projectId',
|
||||
'creationDate',
|
||||
'changeDate',
|
||||
'state',
|
||||
'roleNamesList',
|
||||
'actions',
|
||||
]"
|
||||
[disableWrite]="(['user.grant.write$', 'user.grant.write:' + grantId] | hasRole | async) === false"
|
||||
[disableDelete]="(['user.grant.delete$', 'user.grant.delete:' + grantId] | hasRole | async) === false"
|
||||
[refreshOnPreviousRoutes]="['/grant-create/project/{{projectId}}/grant/{{grantId}}']"
|
||||
|
@ -187,7 +187,7 @@
|
||||
<cnsl-user-grants
|
||||
[context]="UserGrantContext.OWNED_PROJECT"
|
||||
[projectId]="projectId"
|
||||
[displayedColumns]="['select', 'user', 'creationDate', 'changeDate', 'roleNamesList', 'actions']"
|
||||
[displayedColumns]="['select', 'user', 'creationDate', 'changeDate', 'state', 'roleNamesList', 'actions']"
|
||||
[refreshOnPreviousRoutes]="['/grant-create/project/' + projectId]"
|
||||
[disableWrite]="(['user.grant.write$', 'user.grant.write:' + projectId] | hasRole | async) === false"
|
||||
[disableDelete]="(['user.grant.delete$', 'user.grant.delete:' + projectId] | hasRole | async) === false"
|
||||
|
@ -136,7 +136,16 @@
|
||||
<cnsl-user-grants
|
||||
[userId]="user.id"
|
||||
[context]="USERGRANTCONTEXT"
|
||||
[displayedColumns]="['org', 'projectId', 'type', 'creationDate', 'changeDate', 'roleNamesList', 'actions']"
|
||||
[displayedColumns]="[
|
||||
'org',
|
||||
'projectId',
|
||||
'type',
|
||||
'creationDate',
|
||||
'changeDate',
|
||||
'state',
|
||||
'roleNamesList',
|
||||
'actions',
|
||||
]"
|
||||
[disableWrite]="(['user.grant.write$'] | hasRole | async) === false"
|
||||
[disableDelete]="(['user.grant.delete$'] | hasRole | async) === false"
|
||||
>
|
||||
|
@ -222,7 +222,7 @@
|
||||
<cnsl-user-grants
|
||||
[userId]="user.id"
|
||||
[context]="USERGRANTCONTEXT"
|
||||
[displayedColumns]="['select', 'projectId', 'creationDate', 'changeDate', 'roleNamesList', 'actions']"
|
||||
[displayedColumns]="['select', 'projectId', 'creationDate', 'changeDate', 'state', 'roleNamesList', 'actions']"
|
||||
[disableWrite]="(['user.grant.write$'] | hasRole | async) === false"
|
||||
[disableDelete]="(['user.grant.delete$'] | hasRole | async) === false"
|
||||
>
|
||||
|
@ -55,5 +55,6 @@ func UserGrantToPb(grant *query.UserGrant) *auth_pb.UserGrant {
|
||||
ProjectGrantId: grant.GrantID,
|
||||
RoleKeys: grant.Roles,
|
||||
UserType: user.TypeToPb(grant.UserType),
|
||||
State: user.UserGrantStateToPb(grant.State),
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ func UserGrantToPb(assetPrefix string, grant *query.UserGrant) *user_pb.UserGran
|
||||
return &user_pb.UserGrant{
|
||||
Id: grant.ID,
|
||||
UserId: grant.UserID,
|
||||
State: user_pb.UserGrantState_USER_GRANT_STATE_ACTIVE,
|
||||
State: UserGrantStateToPb(grant.State),
|
||||
RoleKeys: grant.Roles,
|
||||
ProjectId: grant.ProjectID,
|
||||
OrgId: grant.ResourceOwner,
|
||||
@ -51,6 +51,21 @@ func UserGrantToPb(assetPrefix string, grant *query.UserGrant) *user_pb.UserGran
|
||||
}
|
||||
}
|
||||
|
||||
func UserGrantStateToPb(state domain.UserGrantState) user_pb.UserGrantState {
|
||||
switch state {
|
||||
case domain.UserGrantStateActive:
|
||||
return user_pb.UserGrantState_USER_GRANT_STATE_ACTIVE
|
||||
case domain.UserGrantStateInactive:
|
||||
return user_pb.UserGrantState_USER_GRANT_STATE_INACTIVE
|
||||
case domain.UserGrantStateRemoved,
|
||||
domain.UserGrantStateUnspecified:
|
||||
// these states should never occur here and are mainly listed for linting purposes
|
||||
fallthrough
|
||||
default:
|
||||
return user_pb.UserGrantState_USER_GRANT_STATE_UNSPECIFIED
|
||||
}
|
||||
}
|
||||
|
||||
func UserGrantQueriesToQuery(ctx context.Context, queries []*user_pb.UserGrantQuery) (q []query.SearchQuery, err error) {
|
||||
q = make([]query.SearchQuery, len(queries))
|
||||
for i, query := range queries {
|
||||
|
@ -799,19 +799,24 @@ func (o *OPStorage) assertRoles(ctx context.Context, userID, applicationID strin
|
||||
if projectID != "" {
|
||||
roleAudience = append(roleAudience, projectID)
|
||||
}
|
||||
queries := make([]query.SearchQuery, 0, 2)
|
||||
projectQuery, err := query.NewUserGrantProjectIDsSearchQuery(roleAudience)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
queries = append(queries, projectQuery)
|
||||
userIDQuery, err := query.NewUserGrantUserIDSearchQuery(userID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
queries = append(queries, userIDQuery)
|
||||
activeQuery, err := query.NewUserGrantStateQuery(domain.UserGrantStateActive)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
grants, err := o.query.UserGrants(ctx, &query.UserGrantsQueries{
|
||||
Queries: queries,
|
||||
Queries: []query.SearchQuery{
|
||||
projectQuery,
|
||||
userIDQuery,
|
||||
activeQuery,
|
||||
},
|
||||
}, true)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
@ -324,10 +324,15 @@ func (p *Storage) getGrants(ctx context.Context, userID, applicationID string) (
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
activeQuery, err := query.NewUserGrantStateQuery(domain.UserGrantStateActive)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p.query.UserGrants(ctx, &query.UserGrantsQueries{
|
||||
Queries: []query.SearchQuery{
|
||||
projectQuery,
|
||||
userIDQuery,
|
||||
activeQuery,
|
||||
},
|
||||
}, true)
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
sd "github.com/zitadel/zitadel/internal/config/systemdefaults"
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
eventstore2 "github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/id"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
@ -119,7 +120,11 @@ func (q queryViewWrapper) UserGrantsByProjectAndUserID(ctx context.Context, proj
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
queries := &query.UserGrantsQueries{Queries: []query.SearchQuery{userGrantUserID, userGrantProjectID}}
|
||||
activeQuery, err := query.NewUserGrantStateQuery(domain.UserGrantStateActive)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
queries := &query.UserGrantsQueries{Queries: []query.SearchQuery{userGrantUserID, userGrantProjectID, activeQuery}}
|
||||
grants, err := q.Queries.UserGrants(ctx, queries, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -143,6 +143,10 @@ func NewUserGrantRoleQuery(value string) (SearchQuery, error) {
|
||||
return NewTextQuery(UserGrantRoles, value, TextListContains)
|
||||
}
|
||||
|
||||
func NewUserGrantStateQuery(value domain.UserGrantState) (SearchQuery, error) {
|
||||
return NewNumberQuery(UserGrantState, value, NumberEquals)
|
||||
}
|
||||
|
||||
func NewUserGrantWithGrantedQuery(owner string) (SearchQuery, error) {
|
||||
orgQuery, err := NewUserGrantResourceOwnerSearchQuery(owner)
|
||||
if err != nil {
|
||||
|
@ -38,6 +38,7 @@ user_grants as (
|
||||
where user_id = $1
|
||||
and instance_id = $2
|
||||
and project_id = any($3)
|
||||
and state = 1
|
||||
{{ if . -}}
|
||||
and resource_owner = any($4)
|
||||
{{- end }}
|
||||
|
@ -1565,6 +1565,11 @@ message UserGrant {
|
||||
description: "type of the user (human / machine)"
|
||||
}
|
||||
];
|
||||
zitadel.user.v1.UserGrantState state = 13 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
description: "current state of the user grant";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
message ListMyProjectOrgsRequest {
|
||||
|
Loading…
Reference in New Issue
Block a user