Filipe Aleixo
Filipe Aleixo

Reputation: 4242

AWS Amplify GraphQL - allow owner to create, but only if he belongs to a certain group

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

Answers (1)

Carlos Espin
Carlos Espin

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

Related Questions