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.
103 lines
2.5 KiB
GraphQL
103 lines
2.5 KiB
GraphQL
type Organization implements Node {
|
|
id: ID!
|
|
name: String!
|
|
logo: File @goField(forceResolver: true)
|
|
horizontalLogo: File @goField(forceResolver: true)
|
|
email: String
|
|
description: String
|
|
websiteUrl: String
|
|
headquarterAddress: String
|
|
createdAt: Datetime!
|
|
updatedAt: Datetime!
|
|
|
|
profiles(
|
|
first: Int
|
|
after: CursorKey
|
|
last: Int
|
|
before: CursorKey
|
|
orderBy: ProfileOrder
|
|
): ProfileConnection @goField(forceResolver: true)
|
|
|
|
samlConfigurations(
|
|
first: Int
|
|
after: CursorKey
|
|
last: Int
|
|
before: CursorKey
|
|
): SAMLConfigurationConnection @goField(forceResolver: true)
|
|
|
|
scimConfiguration: SCIMConfiguration @goField(forceResolver: true)
|
|
scimBridgeTypes: [SCIMBridgeTypeInfo!]! @goField(forceResolver: true)
|
|
|
|
auditLogEntries(
|
|
first: Int
|
|
after: CursorKey
|
|
last: Int
|
|
before: CursorKey
|
|
orderBy: AuditLogEntryOrder
|
|
filter: AuditLogEntryFilter
|
|
): AuditLogEntryConnection! @goField(forceResolver: true)
|
|
|
|
viewer: Profile @goField(forceResolver: true)
|
|
|
|
permission(action: String!, attributes: Map): Boolean!
|
|
@goField(forceResolver: true)
|
|
@authentication(required: PRESENT)
|
|
}
|
|
|
|
extend type Mutation {
|
|
createOrganization(
|
|
input: CreateOrganizationInput!
|
|
): CreateOrganizationPayload @authentication(required: PRESENT)
|
|
updateOrganization(
|
|
input: UpdateOrganizationInput!
|
|
): UpdateOrganizationPayload @authentication(required: PRESENT)
|
|
deleteOrganization(
|
|
input: DeleteOrganizationInput!
|
|
): DeleteOrganizationPayload @authentication(required: PRESENT)
|
|
deleteOrganizationHorizontalLogo(
|
|
input: DeleteOrganizationHorizontalLogoInput!
|
|
): DeleteOrganizationHorizontalLogoPayload @authentication(required: PRESENT)
|
|
}
|
|
|
|
input CreateOrganizationInput {
|
|
name: String!
|
|
logoFile: Upload
|
|
horizontalLogoFile: Upload
|
|
}
|
|
|
|
input UpdateOrganizationInput {
|
|
organizationId: ID!
|
|
name: String
|
|
logoFile: Upload
|
|
horizontalLogoFile: Upload
|
|
description: String @goField(omittable: true)
|
|
websiteUrl: String @goField(omittable: true)
|
|
email: String @goField(omittable: true)
|
|
headquarterAddress: String @goField(omittable: true)
|
|
}
|
|
|
|
input DeleteOrganizationInput {
|
|
organizationId: ID!
|
|
}
|
|
|
|
input DeleteOrganizationHorizontalLogoInput {
|
|
organizationId: ID!
|
|
}
|
|
|
|
type CreateOrganizationPayload {
|
|
organization: Organization
|
|
profile: Profile!
|
|
}
|
|
|
|
type UpdateOrganizationPayload {
|
|
organization: Organization
|
|
}
|
|
|
|
type DeleteOrganizationPayload {
|
|
deletedOrganizationId: ID!
|
|
}
|
|
|
|
type DeleteOrganizationHorizontalLogoPayload {
|
|
organization: Organization!
|
|
}
|