Special Notes on Permissions
Here are some notes and considerations when working with Iridium's authorization system.
Type safety is not guaranteed
Gates are not statically typed behind the scenes to allow maximal flexibility in what arguments you provide.
Iridium has some methods to fake static typing using callback methods that auto-cast based on your provided generics for your gate's definition, but Iridium cannot enforce you call a specific gate with the same types it was defined with. Why?
This means you MUST call your gate with the EXACT types you provided in the definition.
That is obvious, but Iridium itself cannot enforce this for you. Your application will purposely panic (in development mode) if you do not match your definition to allow you to catch this before you ship.
Gates & Policy Ordering
When calling a gate with a particular ability, Iridium will always check if a relevant policy file exists first, before searching for your gate.
Example
Say you define a gate called edit as so:
gate.DefineGate("edit", func(ctx context.CustomContext, args ...interface{}) bool {
// This gate always takes a single appointment argument, so lets cast that
appointment := args[0].(*Appointment)
// If the owner of this appointment is the current user, then they can edit it.
return appointment.Owner.Id == ctx.User().GetId()
}And you have an Appointment Policy defined like so:
type AppointmentPolicy struct {}
func (_ AppointmentPolicy) Edit(ctx context.CustomContext, appointment *Appointment) bool {
// If the appointment's owner is the current user, & the current user's ID is `1`
return appointment.Owner.Id == ctx.User().GetId() && ctx.User().GetId() == 1
}There is a name collision here, and since policy files are searched before gates for a particular name, if you call your gate as
var appointment Appointment // Imagine this is instantiated elsewhere with values
gate.Can(ctx, "edit", appointment)That gate call will only return true if the appointment's owner is the current user, and the current user's Id is 1.
In other-words, it found a matching policy first and hence used that, rather than using your gate.
Avoiding this
It's easy to avoid this. For example, you could have named your gate edit-appointment. Since there is no EditAppointment method in your policy file, it would skip it, and then find your registered gate.
Special behaviour of the first argument
This is worth repeating. The first argument of a gate is special. Iridium will always inspect your first argument as a way to match a related policy.
For example, if you call a gate and provide an Appointment struct as the first argument, Iridium will check to see if you defined a policy related to that struct.
That is how policy files are matched. All further arguments are just provided to the final callback, and have no special properties.