Skip to content

Gates

Gates are small, specific authorization checks you can reuse throughout Iridium. You define a gate with a name, and target(s), and then can call that gate throughtout your application to perform a permissions check.

INFO

If you need a set of permissions for your resource that map directly to traditional CRUD application and a particular model (rules for who can create, delete, edit, etc, a particular model), please see policies instead.

Creating a basic gate

To create a basic gate, you will provide a name and a callback function to the perform your permission check. That callback returns a bool indicating if the permission check was passed, or it failed.

You can do this by calling the DefineGate method as so:

go
import	"github.com/iridiumgo/iridium/bootstrap/auth/gate"

gate.DefineGate("my-custom-gate", func(ctx context.CustomContext, args ...interface{}) bool {
    // Your permission check here.
    // return true or false based on the results
})
  • context.CustomContext -> A general context interface. You can access the http request & writer objects, as well as your logged in user's model.
  • args -> A list of arguments you provide when calling your gate. You can pass anything in here, but you are required to cast them to use them.

Call a gate

You can call a gate simply with:

go
import	"github.com/iridiumgo/iridium/bootstrap/auth/gate"

gate.Call(ctx, "my-custom-gate", "Joe", 25)

Explanation:

  • ctx -> the context.CustomContext interface. You can provide any of Iridium's contexts here.
  • "my-custom-gate" -> The name of your gate you defined elsewhere
  • "Joe" -> A string argument used in your gate's definition
  • 25 -> A random int used in your gate's definition

Gate arguments

Gate arguments can by anything since they map directly to ...interface{}. You'll want to cast them to use them properly in your definition however.

Gate without arguments

If you do not require arguments for your gate, you can instead call the DefineSimpleGate method.

go
gate.DefineSimpleGate("my-custom-gate", func(ctx context.CustomContext) bool {
    // No arguments required for your gate
})

Calling a Simple Gate

You can call a simple gate the same as a traditional gate. All arguments are ignored, so you can set these to nil.

go
gate.Can(ctx, "my-custom-gate", nil)

Gate Example

Here is an example of defining and using a gate.

A traditional gate example

In this example, we're defining a custom gate to check if the currently logged in user can rent a car.

To be able to rent a car, you need to:

  1. Have the admin role
  2. Be above the age of 25

Notes:

  • Please not that in this example we're casting the ctx.User() method to a custom models.User. That models.User is not an iridium type. That is your custom user struct you define. You need to register it to perform this cast. Please see more here
go
import	"github.com/iridiumgo/iridium/bootstrap/auth/gate"

// Defining a gate that makes sure a user has the `admin` role, and is over a certain age
gate.DefineGate("rent-car", func(ctx context.CustomContext, args ...interface{}) bool {
    // Get the age from the first gate argument (must cast to an int)
    age := args[0].(int)
    // Get the value of the current user in our ctx.
    // Cast that user to your custom model struct to access its fields
    user := ctx.User().(*models.User)
    if user.Role != "admin" {
        return false
    }
    if user.Age <= age {
        return false
    }
    return true
})

// form.go <- An example form
Form().
    Schema(
        FormInput("age").
            Decimal(),
        // Users should only be able to see this if they are an Admin and over 25
        FormSwitch("rent-a-car").
            HiddenFn(func (ctx FormContext) bool {
               // Get the age the user entered
               selectedAge, err := ctx.Get("age")
               // If err or age is empty, then hide this switch
               if err != nil || selectedAge == "" {
                    return false
               }
               var ageInt int
               ageInt, err = strconv.Atoi(s)
               // If age is not a integer, then return false
               if err != nil {
                   return false
               }
               // check if the current user can rent a car
               return gate.Can(ctx, "rent-car", ageInt)            
            }),
    )

Typing Gates

Iridium offers methods when defining gates to statically type your expected context, user model, and an argument. There is some important information here about how Iridium handles this.

Fully typing gates does not allow for variadic arguments. You need to provide one argument struct in order to preserve your typing.

DefineTypedGate

You can fully type your gate definition with the DefineTypedGate method as so:

go
gate.DefineTypedGate[Ctx, Model, Argument]("custom-gate", func(ctx Ctx, model Model, arg Argument)bool{
    // You know have access to a typed context, model, and argument in here
})

DefineSimpleTypedGate

If your gate does not require arguments but you still want to enforce the context and user type, you can do so as follows:

go
gate.DefineSimpleTypedGate[Ctx]("simple-custom-gate", func(ctx Ctx) bool {
    // You now have access to a typed context in here. 
})

Naming

Gate names must be unique across your application.

Gate Methods

Can

Can will check whether the current user is authorized for an ability.

Cannot

Cannot will check whether the current user is not authorized for an ability.

All

All will check whether the current user is authorized for all listed abilities

All will apply the same user and arguments to each listed ability. If you require different arguments for each ability, you'll need to write your own implementation.

Inspect

Inspect will return additional information in the form of a Response object about the particular check.

This is useful for debugging to see where, either via a policy, gate, or hook, your authorization check passed or failed.

HasAbilities

HasAbilities will check to see if the user can pass all listed checks. This short-circuits on a failure.

HasAbilities will apply the same user and arguments to each listed ability. If you require different arguments for each ability, you'll need to write your own implementation.

Released under the MIT License.