auth

package module
v0.0.2-beta Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 20, 2023 License: MIT Imports: 14 Imported by: 1

README

Auth

Go Reference

Easy to use postgres backed authentication service for go.

Features

  • Authentication
  • Users
  • Groups
  • Permissions
  • Sessions
  • Rate limiting (with redis)
  • Password Resets
  • Registration Confirmations

Installation

go get -u github.com/cristosal/auth

Documentation

View the godoc documentation here

https://pkg.go.dev/github.com/cristosal/auth

Usage

Create a new service using an existing *sql.DB

db, _ := sql.Open("pgx", os.Getenv("CONNECTION_STRING"))

authService := auth.NewService(db)

You now have access to the various underlying apis

// users api
authService.Users()

// permissions api 
authService.Permissions()

// groups api
authService.Groups()

// sessions api
authService.Sessions()

If you want to use rate limiting, pass in a redis client

rcl := redis.NewClient(&redis.Options{Addr: os.Getenv("REDIS_ADDR")})

limiter := auth.NewRedisRateLimiter(rcl)

Documentation

Index

Constants

View Source
const (
	SessionDuration     = time.Hour * 3
	SessionLongDuration = time.Hour * 24 * 30
)
View Source
const (
	PageSize = 25
)
View Source
const PasswordHashCost = 10
View Source
const TokenDuration = time.Hour

Variables

View Source
var (
	ErrGroupNotFound      = errors.New("group not found")
	ErrEmailRequired      = errors.New("email is required")
	ErrInvalidToken       = errors.New("invalid token")
	ErrNameRequired       = errors.New("name is required")
	ErrPasswordRequired   = errors.New("password is required")
	ErrPermissionNotFound = errors.New("permission not found")
	ErrSessionNotFound    = errors.New("session not found")
	ErrSessionExpired     = errors.New("session expired")
	ErrTokenExpired       = errors.New("token expired")
	ErrTokenNotFound      = errors.New("token not found")
	ErrUnauthorized       = errors.New("unauthorized")
	ErrUserExists         = errors.New("user exists")
	ErrUserNotFound       = errors.New("user not found")
)

package wide errors go here

View Source
var (
	ErrLimitReached = errors.New("limit reached")
)

Functions

func GenerateToken

func GenerateToken(bytes int) (string, error)

Types

type Group

type Group struct {
	ID          int64
	Name        string
	Description string
	Priority    int
}

func (*Group) NewPermission

func (g *Group) NewPermission(p *Permission, v int) *GroupPermission

func (*Group) TableName

func (*Group) TableName() string

type GroupPermission

type GroupPermission struct {
	GroupID      int64
	PermissionID int64
	Priority     int    `db:"-"` // group priority value
	Name         string `db:"-"` // permission name
	Value        int
}

GroupPermission represents the union between a group and a permission it can contains a value for use in application logic

func (*GroupPermission) TableName

func (*GroupPermission) TableName() string

type GroupPermissions

type GroupPermissions []GroupPermission

func (GroupPermissions) Has

func (gps GroupPermissions) Has(name string) bool

func (GroupPermissions) Value

func (gps GroupPermissions) Value(name string) int

Value returns the value associated with the permission of a given name. it takes into account conflicting permissions and takes the one with higher priority

type GroupRepo

type GroupRepo struct {
	// contains filtered or unexported fields
}

GroupRepo us a group repository using pgx

func NewGroupRepo

func NewGroupRepo(db orm.DB) *GroupRepo

func (*GroupRepo) Add

func (r *GroupRepo) Add(g *Group) error

Add adds a group

func (*GroupRepo) AddPermission

func (r *GroupRepo) AddPermission(gid, pid int64, value int) error

func (*GroupRepo) AddUser

func (r *GroupRepo) AddUser(uid int64, gid int64) error

AddUser adds a user to a group. No error will occur if a user is already part of the group

func (*GroupRepo) ByID

func (r *GroupRepo) ByID(id int64) (*Group, error)

GroupByID returns a group by it's id

func (*GroupRepo) ByName

func (r *GroupRepo) ByName(name string) (*Group, error)

GroupByName finds a group by it's name

func (*GroupRepo) ByUser

func (r *GroupRepo) ByUser(uid int64) (Groups, error)

GroupsByUser returns all groups that user is a part of

func (*GroupRepo) List

func (r *GroupRepo) List() (Groups, error)

Groups returns all groups ordered by priority (highest first)

func (*GroupRepo) Permissions

func (r *GroupRepo) Permissions(gid int64) (GroupPermissions, error)

Permissions returns group permissions for a group by group id

func (*GroupRepo) Remove

func (r *GroupRepo) Remove(gid int64) error

Remove deletes a group by id

func (*GroupRepo) RemovePermission

func (r *GroupRepo) RemovePermission(gid, pid int64) error

func (*GroupRepo) RemoveUser

func (r *GroupRepo) RemoveUser(uid int64, gid int64) error

RemoveUser removes a user from a group

func (*GroupRepo) Seed

func (r *GroupRepo) Seed(groups []Group) error

Seed seeds groups to the database. If they already exist it will not return an error

func (*GroupRepo) Update

func (r *GroupRepo) Update(g *Group) error

Update updates a group with name and priority

func (*GroupRepo) UserCount

func (r *GroupRepo) UserCount(gid int64) (int, error)

GroupUserCount counts all users within a group

func (*GroupRepo) UserPermissions

func (r *GroupRepo) UserPermissions(uid int64) (GroupPermissions, error)

func (*GroupRepo) Users

func (r *GroupRepo) Users(gid int64) ([]User, error)

GroupUsers returns all users within a group

type Groups

type Groups []Group

type Limiter

type Limiter interface {
	// Limit increases the hit count for a given key.
	// When count value reaches max within window duration, MaxAttemptsError is returned until window has elapsed.
	Limit(key string, max int, window time.Duration) error

	// TTL returns the time to live for the limit block.
	// Returns 0 duration if limit has not yet been reached.
	TTL(key string, max int) (ttl time.Duration)

	// Reset resets the limit for a given key.
	Reset(key string) error
}

Limiter is the interface implemented by rate limiters

type PasswordReset

type PasswordReset struct {
	Token    string
	Password string
}

type PasswordResetToken

type PasswordResetToken struct {
	UserID  int64
	Email   string
	Token   string
	Expires time.Time
}

func (PasswordResetToken) TableName

func (PasswordResetToken) TableName() string

type Permission

type Permission struct {
	ID          int64
	Name        string
	Description string
	Type        PermissionType
}

Permission represents a users access to a resource.

func (*Permission) TableName

func (p *Permission) TableName() string

type PermissionRepo

type PermissionRepo struct {
	// contains filtered or unexported fields
}

func NewPermissionRepo

func NewPermissionRepo(db orm.DB) *PermissionRepo

func (*PermissionRepo) Add

func (s *PermissionRepo) Add(p *Permission) error

func (*PermissionRepo) ByID

func (s *PermissionRepo) ByID(id int64) (*Permission, error)

func (*PermissionRepo) ByName

func (s *PermissionRepo) ByName(name string) (*Permission, error)

func (*PermissionRepo) Clear

func (s *PermissionRepo) Clear() error

func (*PermissionRepo) List

func (s *PermissionRepo) List() (Permissions, error)

List lists all permissions

func (*PermissionRepo) Remove

func (s *PermissionRepo) Remove(id int64) error

func (*PermissionRepo) RemoveByName

func (s *PermissionRepo) RemoveByName(name string) error

func (*PermissionRepo) Seed

func (r *PermissionRepo) Seed(permissions []Permission) error

func (*PermissionRepo) Update

func (s *PermissionRepo) Update(p *Permission) error

type PermissionType

type PermissionType = string
const (
	Quantity PermissionType = "quantity"
	Access   PermissionType = "access"
)

type Permissions

type Permissions []Permission

Permissions is a collection of permission

func (Permissions) Has

func (p Permissions) Has(name string) bool

Has returns true when a permission with a given key is found and it's value is greater than 0

type RedisLimiter

type RedisLimiter struct {
	// contains filtered or unexported fields
}

RedisLimiter is the implementation for Limiter using redis as cache

func NewRedisLimiter

func NewRedisLimiter(cl *redis.Client) *RedisLimiter

NewRedisLimiter returns a Limiter implementation using redis as the underlying cache store

func (*RedisLimiter) Limit

func (l *RedisLimiter) Limit(key string, max int, window time.Duration) error

Limit is the implementation of Limiter interface. It returns ErrLimitReached when attempts have been exceeded.

func (*RedisLimiter) Reset

func (l *RedisLimiter) Reset(key string) error

func (*RedisLimiter) TTL

func (l *RedisLimiter) TTL(key string, max int) (ttl time.Duration)

type RegistrationRequest

type RegistrationRequest struct {
	Name     string
	Email    string
	Phone    string
	Password string
}

type RegistrationResponse

type RegistrationResponse struct {
	UserID int64
	Name   string
	Email  string
	Phone  string
	Token  string
}

type RegistrationToken

type RegistrationToken struct {
	UserID  int64
	Email   string
	Token   string
	Expires time.Time
}

func (RegistrationToken) TableName

func (RegistrationToken) TableName() string

type Service

type Service struct {
	// contains filtered or unexported fields
}

func NewService

func NewService(db orm.DB) *Service

func (*Service) Groups

func (s *Service) Groups() *GroupRepo

func (*Service) Init

func (s *Service) Init() error

func (*Service) Permissions

func (s *Service) Permissions() *PermissionRepo

func (*Service) Sessions

func (s *Service) Sessions() *SessionRepo

func (*Service) Users

func (s *Service) Users() *UserRepo

type Session

type Session struct {
	ID          string           `json:"id"`
	Counter     int              `json:"counter"` // the amount of times the session has been saved
	User        *User            `json:"user,omitempty"`
	Groups      Groups           `json:"groups,omitempty"`
	Permissions GroupPermissions `json:"permissions,omitempty"`
	ExpiresAt   time.Time        `json:"expires_at"`
	UserAgent   string           `json:"user_agent"`
	Message     string           `json:"message"`
	MessageType string           `json:"message_type"`
	IP          string           `json:"ip"` // Source IP Address
	Meta        map[string]any   `json:"meta"`
}

func NewSession

func NewSession(expiresAt time.Time) Session

func (*Session) ClearFlash

func (s *Session) ClearFlash()

func (*Session) Expired

func (s *Session) Expired() bool

func (*Session) Flash

func (s *Session) Flash(msgtype, msg string)

func (*Session) Get

func (s *Session) Get(key string) any

func (*Session) GroupName

func (s *Session) GroupName() string

func (*Session) HasFlash

func (s *Session) HasFlash() bool

func (Session) IsAnonymous

func (s Session) IsAnonymous() bool

func (Session) IsAuthorized

func (s Session) IsAuthorized() bool

func (Session) MarshalBinary

func (s Session) MarshalBinary() ([]byte, error)

func (*Session) Set

func (s *Session) Set(key string, data any)

func (*Session) UnmarshalBinary

func (s *Session) UnmarshalBinary(data []byte) error

func (*Session) UserID

func (s *Session) UserID() *int64

type SessionRepo

type SessionRepo struct {
	// contains filtered or unexported fields
}

SessionRepo is a postgres backed session store

func NewSessionRepo

func NewSessionRepo(db orm.DB) *SessionRepo

NewSessionRepo returns postgres backed session store

func (*SessionRepo) ByID

func (s *SessionRepo) ByID(sessionID string) (*Session, error)

ByID returns a session by its id

func (*SessionRepo) ByUserID

func (s *SessionRepo) ByUserID(uid int64) ([]Session, error)

ByUserID returns all sessions belonging to a user

func (*SessionRepo) Drop

func (s *SessionRepo) Drop() error

Drop drops the session table

func (*SessionRepo) RemoveByEmails

func (s *SessionRepo) RemoveByEmails(emails []string) error

DeleteByUserID deletes all sessions for users in the email list

func (*SessionRepo) RemoveByID

func (s *SessionRepo) RemoveByID(id string) error

Remove session by id

func (*SessionRepo) RemoveByUserID

func (s *SessionRepo) RemoveByUserID(uid int64) error

RemoveByUserID deletes all sessions for a given user

func (*SessionRepo) RemoveExpired

func (s *SessionRepo) RemoveExpired() error

RemoveExpired deletes all sessions which have expired

func (*SessionRepo) Save

func (s *SessionRepo) Save(sess *Session) error

Save upserts session into database

type User

type User struct {
	ID          int64
	Name        string
	Email       string
	Phone       string
	Password    string `json:"-"`
	ConfirmedAt *time.Time
	LastLogin   *time.Time
	CreatedAt   *time.Time
}

func (*User) Confirm

func (u *User) Confirm()

func (*User) IsConfirmed

func (u *User) IsConfirmed() bool

func (*User) TableName

func (u *User) TableName() string

func (*User) VerifyPassword

func (u *User) VerifyPassword(pass string) bool

type UserRepo

type UserRepo struct {
	// contains filtered or unexported fields
}

func NewUserRepo

func NewUserRepo(db orm.DB) *UserRepo

func (*UserRepo) Authenticate

func (r *UserRepo) Authenticate(email, pass string) (*User, error)

func (*UserRepo) ByEmail

func (r *UserRepo) ByEmail(email string) (*User, error)

ByEmail returns a user by email

func (*UserRepo) ByID

func (r *UserRepo) ByID(id int64) (*User, error)

ByID returns a user by id field

func (*UserRepo) ConfirmPasswordReset

func (r *UserRepo) ConfirmPasswordReset(reset *PasswordReset) (*User, error)

ConfirmPasswordReset

func (*UserRepo) ConfirmRegistration

func (r *UserRepo) ConfirmRegistration(tok string) (*User, error)

ConfirmRegistration confirms a users account if a registration token is found matching tok

func (*UserRepo) Paginate

func (r *UserRepo) Paginate(page int, q string) ([]User, *orm.PaginationResults, error)

Paginate paginates through users returning the most recent ones first

func (UserRepo) PasswordHash

func (UserRepo) PasswordHash(pass string) (string, error)

PasswordHash performs a bcrypt hash for the password based on PasswordHashCost

func (*UserRepo) Register

func (*UserRepo) RenewRegistration

func (r *UserRepo) RenewRegistration(uid int64) (*RegistrationToken, error)

RenewRegistration generates another registration token for the given user. Returns ErrTokenNotFound if a registration token was not available. To issue a renewal, a token must have already been generated

func (*UserRepo) RequestPasswordReset

func (r *UserRepo) RequestPasswordReset(email string) (*PasswordResetToken, error)

func (*UserRepo) ResetPassword

func (r *UserRepo) ResetPassword(uid int64, pass string) error

func (UserRepo) SanitizeEmail

func (UserRepo) SanitizeEmail(email string) string

func (*UserRepo) UpdateInfo

func (r *UserRepo) UpdateInfo(u *User) error

UpdateInfo updates the users info, excluding the password

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL