Reputation: 4242
Suppose I have three user groups: admin
, doctor
, and patient
I want both admin
and doctor
to be able to create the type Appointment
, but I want to forbid patient
to do it. To simplify let's suppose that anyone from the 3 groups can read any Appointment
.
Here's the tricky part:
For each Appointment
already created I want the admin group be able to update it and delete it (even if the admin
is not the owner of that Appointment
), but for the doctor
group I want to only allow access to update if the doctor is the owner of the appointment. Besides admin
, I want to let only users in the doctor
group to be able to create Appointment
. The part in bold is what I'm being unable to do. In the schema I have below, patients are also able to create appointments.
How do I change it so that only doctor
and admin
can create?
Note: in Appointment
the create operation for owner
must be there, otherwise, the clientId
field doesn't auto-populate with the owner id, and I need that. I think that create
operation is actually what's creating my problem, but I see no way around.
type Doctor
@model
@auth(
rules: [
{ allow: private, operations: [read] }
{ allow: groups, groups: ["admin"], operations: [create, update, delete] }
{ allow: owner, ownerField: "doctorId", operations: [update] }
]
)
@key(fields: ["doctorId"]) {
doctorId: ID! # This ID won't be auto-populated (it's the cognito pool ID of the doctor)
name: String!
appointments: [Appointment]
@connection(keyName: "byDoctor", fields: ["doctorId"])
createdAt: String
}
type Patient
@model
@auth(
rules: [
{ allow: private, operations: [read] }
{ allow: groups, groups: ["admin"], operations: [create, update, delete] }
{ allow: owner, ownerField: "patientId", operations: [update] }
]
)
@key(fields: ["patientId"]) {
patientId: ID! # This ID won't be auto-populated (it's the cognito pool ID of the patient)
name: String!
appointments: [Appointment]
@connection(keyName: "byPatient", fields: ["patientId"])
createdAt: String
}
type Appointment
@model
@auth(
rules: [
{ allow: private, operations: [read] }
{ allow: groups, groups: ["admin"], operations: [create, update, delete] }
{ allow: groups, groups: ["doctor"], operations: [create, read] }
{ allow: owner, ownerField: "doctorId", operations: [create, update] }
]
)
@key(
name: "byDoctor"
fields: ["doctorId", "createdAt"]
queryField: "appointmentsByDoctorId"
)
@key(
name: "byPatient"
fields: ["patientId", "createdAt"]
queryField: "appointmentsByPatientId"
) {
id: ID!
doctorId: ID # Auto-populated with cognito pool ID of the doctor
patientId: ID!
doctor: Doctor @connection(fields: ["doctorId"])
patient: Patient @connection(fields: ["patientId"])
createdAt: String
}
And here's an illustration of the problem:
# -- Initially authenticated as a user in `admin` group --
# Result: initially empty (which is expected)
query ListAppointments {
listAppointments {
items {
id
doctorId
patientId
}
}
}
# Result: succeeds (which is expected)
mutation CreateDoctor {
createDoctor(input: {
doctorId: "66a2b382-952d-4e8d-a25e-b8d1876e8017" # arbitrary
name: "Doctor 1"
}) {
doctorId
name
}
}
# Result: succeeds (which is expected)
mutation CreatePatient {
createPatient(input: {
patientId: "2d9366cd-165b-4437-a910-647900df0587" # arbitrary
name: "Bar Patient"
}) {
patientId
name
}
}
# -- Switching auth to user in `doctor` group --
# Result: succeeds (which is expected)
# If we run `ListAppointments` now, the appointment created is there
mutation CreateAppointmentByDoctor {
createAppointment(input: {
patientId: "8a2b382-952d-4e8d-a25e-b8d187000000" # arbitrary
}) {
id
}
}
# -- Switching auth to user in `patient` group --
# Result: succeeds (which is NOT expected)
# If we run `ListAppointments` now, there are two appointments there,
# including this one created by a client
mutation CreateAppointmentByPatient {
createAppointment(input: {
patientId: "8a2b382-952d-4e8d-a25e-b8d187001000" #arbitrary
}) {
id
}
}
Upvotes: 0
Views: 1372
Reputation: 119
I think you need a custom resolver to handle this case:
https://docs.amplify.aws/cli/graphql-transformer/resolvers
If you want some code examples look at the build folder inside the api folder in the project.
Amplify templates the code of the resolvers based on the directives you're putting in the schema.
You can reuse part of this code to implement your use case in a custom resolver.
Upvotes: 1