Categorygithub.com/angadn/auth
modulepackage
0.0.0-20210211163939-e0b7b9b688c5
Repository: https://github.com/angadn/auth.git
Documentation: pkg.go.dev

# README

auth

A package to provide user-authentication as well as authorization (at some point in time).

Authentication

var (
    err  error
    user User
)

defer session.Cancel() // Will write internal `err` as Response, in case of error.

if user, err := session.Auth(); err != nil {
    log.Printf(err.Error())
    return
}

...

Authorization

A simple, familiar system inspired by the likes of Google Cloud Platform, BitBucket, and Atlassian Confluence.

Philosophy

auth aims to simply provide a way to persist various roles a user may be assigned for resources. It doesn't implicitly infer any hierarchy among these roles or resources. For example, whether users with the role of Owners are allowed to do everything those with the role of Editors can, is left up to your business-logic. The idea is to keep our persistence of authorization as light as possible, so as to avoid the any complex migrations to them in case the rules change on us.

Resources are themselves more often than not hierarchical in nature - i.e. an Account can contain multiple Campaigns, and therefore an Editor of Account is also an Editor of it's underlying Campaigns. However, we steer clear of any such rule-definitions in our framework. This allows the developer to build both, implicitly whitelisting, as well as explicitly blacklisting systems as she may deem fit for her use-case.

This impedes us from providing certain auto-magic out-of-the-box, like disallowing a end-user from deleting herself from an Owners group. Rather, it transfers this responsibility to the developer, who may choose to allow it (creating a sophisticated system for ownership transfers), or disallow it.

Lastly, we make a rather bold deviation from most authorization-frameworks by not even persisting what actions a Role may allow a User to perform upon a Resource - such as Create, Read, Update, Delete or a combination of the aforementioned. This is largely because it is often unnatural for actions to be labeled so. Take for instance an email-sending system - we can easily see how an eXecute label would be required for creating a sophisticated system. For other Resources within the same system, this label would make little sense. As fewer things are scarcer that discipline among software-developers, we steer clear of a situation where system-wide changes would require us to relabel all the persisted actions, by not persisting actions to begin with. In sophisticated systems, forcing CRUD labels onto Roles create more problems than they would solve.

Ubiquitous Language

  • A Resource represents an Entity in the business-layer.
  • A Role represents a logical set of allowed actions on a Resource.
  • A Group is a set of Users for a Role.
  • A User is allowed to perform an action if present in one or more Groups for the corresponding Role(s).

Usage

Inside Campaign:

func (campaign Campaign) Resource() (kind auth.ResourceKind, id auth.ResourceID) {
    return ResourceKind, auth.ResourceID(campaign.ID)
}

func (campaign Campaign) NewOwnerRole() (ownerRole auth.Role) {
    return NewRole(OwnerRole, campaign.Resource())
}

func (campaign Campaign) OwnerRoles() (ownerRoles auth.Roles) {
    ownerRoles = append(
        ownerRoles,
        campaign.NewOwnerRole(),
        campaign.User.OwnerRoles()..., // Refers to the parent account
    )

    return
}

Now, while creating a new Campaign:

// Add the creator of a Campaign to it's 'Owner' group.
auth.Groups.Add(ctx, user, campaign.NewOwnerRole())

Sometime later, in our Services...

// Check
if ok, err = auth.Groups.Belongs(ctx, user, campaign.OwnerRoles()); err != nil {
    return
} else if !ok {
    err = fmt.Errorf("only owner(s) can perform this action")
    return
}

...

# Functions

ConfigMaster sets the details for the user that has access to everything.
FromContext creates a User from a context.Context.
GRPCAuthFunc matches grpc_auth.AuthFunc, in case you want to use github.com/grpc-ecosystem/go-grpc-middleware.
Hash is a convenience function that appends the password along with any other passed salts to return an SHA1 hash.
HTTPHandler to chain with our HandlerFuncs, performing Auth before invoking them.
IsMaster is a convenience-method to check whether the User is Master or not.
NewAWSCognitoRBAC is the provider for an AWS Cognito-backed Repository.
NewGroupMySQLRepositoryImpl is a constructor for GroupMySQLRepository.
NewGRPCSession is a constructor for GRPCSession.
NewHTTPSession is a constructor for HTTPSession.
NewQuery is a constructor for Query.
NewRole is a convenience-constructor for Role.
RolesFor is a convenience-constructor for constructing an array of Roles with the same name but for different Resources.
WithGroupRepository configures the GroupRepository implementation that `auth` will refer.
WithRBAC configures the RBAC implementation that `auth` will refer.
WithRepository configures the User Repository implementation that `auth` will refer.

# Constants

IgnoreUnverified and pass Auth.
UserKey for storing auth.Auth with context.WithValue(...).

# Variables

AWSCognitoModule is an fx.Options that sets up our AWS Cognito RBAC.
ErrInvalidUserCredentials when ID, Secret doesn't match that in the database.
ErrMissingUserCredentials when auth information isn't present in message.
ErrUnauthorizedUser when User lacks necessary permissions to make a request.
ErrUserNotVerified when user hasn't verified email ID, phone, etc.
Groups exposes our internal GroupRepository as a public API for our business layer.
GRPCUnaryInterceptor to Auth incoming requests.
Module is an fx.Options that includes provider (constructors) and invoke (register) functions of the package.
Owner is the role that has access to all resources.
PlatformResource is the top level Resource that contains all other Resources.

# Structs

AWSCognitoRBAC implements the RBAC interface for AWS Cognito.
Group of Users with a common Role.
GroupMySQLRepository implements GroupRepository in MySQL.
GroupRepository persists our Groups using an underlying GroupRepositoryImpl.
GRPCSession is an implementation of Session for gRPC, and checks for User and Secret in the Auth protobuf-generated message-struct.
HTTPSession implements Session over HTTP headers.
Query returns a fragment that can be used to authenticate resource access.
RBACUser provides stubs for User#Secret and User#IsVerified as RBAC-implementations often do not maintain these values as part of their business logic, but instead delegate it to their RBAC system.
Role represents a realm of allowed actions that it allows upon a Resource.

# Interfaces

GroupRepositoryImpl defines an interface with which we can persist our Groups.
GRPCUnaryInterceptorOverride is an interface a gRPC Server can implement to override the global server GRPCUnaryInterceptor installation and implement custom authentication.
RBAC is an interface that any RBAC provider must implement.
Repository is the interface for your application's repository to implement.
Resource is an interface that must be implemented by entities.
Session for an authenticated User.
User is a generic type that requires your application-level Users to implement certain.

# Type aliases

Key is a non-simple type for keys in the context.Context operations by Sessions.
Option to configure different behaviour for Session#Auth.
ResourceID to identify Resources.
ResourceKind to get the type of a Resource.
RoleName is a string-based key to ensure our Roles are named uniquely through our codebase.
Roles is a type-alias for []Role.