Replace the deny-based restriction on granting OWNER with role-scoped allow policies so authorization fails closed: admins may create and update memberships only when the assigned role is not OWNER, and the absence of a target role no longer implies permission. To keep console UI gating accurate without loosening the base grants, the permission field gains an optional typed options argument (PermissionOptionsInput) that forwards target_role into the dry-run authorization. Only the two role-related console calls (create user, update membership) pass it; the OWNER option stays hidden for admins via the existing assignable-roles helper. Add a non-regression test that an admin cannot promote a member to OWNER while still being able to change members between non-owner roles.
37 lines
1.0 KiB
GraphQL
37 lines
1.0 KiB
GraphQL
enum MembershipRole
|
|
@goModel(model: "go.probo.inc/probo/pkg/coredata.MembershipRole") {
|
|
OWNER @goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleOwner")
|
|
ADMIN @goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleAdmin")
|
|
EMPLOYEE
|
|
@goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleEmployee")
|
|
VIEWER @goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleViewer")
|
|
AUDITOR
|
|
@goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleAuditor")
|
|
}
|
|
|
|
type Membership implements Node {
|
|
id: ID!
|
|
createdAt: Datetime!
|
|
role: MembershipRole!
|
|
|
|
lastSession: Session @goField(forceResolver: true)
|
|
|
|
permission(action: String!, attributes: Map): Boolean!
|
|
@goField(forceResolver: true)
|
|
@authentication(required: PRESENT)
|
|
}
|
|
|
|
extend type Mutation {
|
|
updateMembership(input: UpdateMembershipInput!): UpdateMembershipPayload!
|
|
}
|
|
|
|
input UpdateMembershipInput {
|
|
organizationId: ID!
|
|
membershipId: ID!
|
|
role: MembershipRole!
|
|
}
|
|
|
|
type UpdateMembershipPayload {
|
|
membership: Membership!
|
|
}
|