gus

package module
v0.0.0-...-5f51165 Latest Latest
Warning

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

Go to latest
Published: Sep 18, 2018 License: Apache-2.0 Imports: 13 Imported by: 0

README

gus

gus

A simple user authentication and account management library with go/sql compatible implementations.

Benefits

  • No http or UI dependencies
  • Ideal for lightweight applications
  • Can use default sqlite3 driver without setting up a database
  • Low resource requirements
  • Easily compose in an existing application or extend to add additional functionality

Features

  • Local user authentication
    • Sign-up, Sign-in
    • Change and reset password
    • PLANNED: Locking with Rate limit locking
  • User management
  • Basic Organisation management

Get started

go get github.com/rjarmstrong/gus 

Set-up database

docker run --rm --name mysql -e MYSQL_ROOT_PASSWORD=rootPassword -p 127.0.0.1:3306:3306 -d mysql:5.7.18

Once the local instance is running create a new database:

docker exec mysql mysql -u root -prootPassword -e "CREATE DATABASE gus2"

| Note: In production don't use the password on the CLI and create a web user with a good password.

Integrate with package

 // Min config
 dsn := fmt.Sprintf("%s:%s@tcp(127.0.0.1:%s)/gus?parseTime=true&multiStatements=true", "root", "", "3306")
 o := gus.DbOptions{DataSourceName: dsn, Seed: true}
 db = gus.GetDb(o)
 users := gus.NewUsers(db)
	
 // Create user
 p := CreateUserParams{Email:"some@email.com"}
 user, tempPass, err := users.Create(p)
	
 // Sign-in
 p := &gus.SignInParams{Email:user.Email, Password:tempPass}
 u, err := users.Authenticate(*p)

Logging

By default debug logging is enabled you can either provide your own implementation *log.Logger

gus.DebugLogger = log.New(os.Stdout, "GUS: ", log.Ldate|log.Lmicroseconds|log.Lshortfile)

Or set it to nil to disable logging

gus.DebugLogger = nil

The same goes for gus.ErrorLogger

Documentation

Index

Constants

View Source
const SeedMySql = `` /* 1421-byte string literal not displayed */
View Source
const SeedSqlLite = `` /* 1160-byte string literal not displayed */

Variables

View Source
var (
	ErrNotAuth         = &NotAuthenticatedError{}
	ErrNotFound        = &NotFoundError{}
	ErrCantDeleteSelf  = ErrInvalid("You can't delete yourself.")
	ErrCantSuspendSelf = ErrInvalid("You can't suspend yourself.")
	ErrTokenExpired    = ErrInvalid("That access token has expired.")
)
View Source
var (
	ErrEmailTaken              = ErrInvalid("That email is taken.")
	ErrUsernameTaken           = ErrInvalid("That username is taken.")
	ErrEmailInvalid            = ErrInvalid("'email' invalid.")
	ErrEmailRequired           = ErrInvalid("'email' required.")
	ErrUsernameRequired        = ErrInvalid("'username' required.")
	ErrUsernameOrEmailRequired = ErrInvalid("'username' or 'email' required.")
	ErrPasswordRequired        = ErrInvalid("'password' required.")
	ErrInvalidResetToken       = ErrInvalid("Invalid reset token.")
	ErrPasswordInvalid         = ErrInvalid(
		"'new_password' must contain: 1 Upper, 1 Lower, 1 Number, 1 Special and 8 Chars",
		"OR any alphanumeric with a minimum of 15 chars.")
)
View Source
var (
	Rgx_ValidPasswordChars = regexp.MustCompile("[a-z0-9A-Z\" !#$%&'()*+,\\-.\\/:;<=>?@\\[\\]^_\\`{\\|}~\\\\]+")
	Rgx_OneLower           = regexp.MustCompile(`[a-z]+`)
	Rgx_OneUpper           = regexp.MustCompile(`[A-Z]+`)
	Rgx_OneSpecial         = regexp.MustCompile("[\" !#$%&'()*+,\\-.\\/:;<=>?@\\[\\]^_\\`{\\|}~\\\\]+")
	Rgx_OneNumeric         = regexp.MustCompile(`\d+`)
	Rgx_PasswordLength     = regexp.MustCompile(`^.{8,30}$`)
)
View Source
var (
	ErrNameRequired error = ErrInvalid("'name' required.")
)
View Source
var ErrorLogger = log.New(os.Stderr, "GUS ERR: ", log.Ldate|log.Lmicroseconds|log.Lshortfile)

Functions

func ApplyUpdates

func ApplyUpdates(original interface{}, updates interface{}) error

ApplyUpdates will apply updates to an 'original' struct and update fields based on an 'updates' struct The 'updates' struct should have point fields and should also serialize to and from json the same as the Intended destination fields.

func CheckNotFound

func CheckNotFound(err error) error

func CheckUpdated

func CheckUpdated(res sql.Result, err error) error

func Debug

func Debug(in ...interface{})

func ErrInvalid

func ErrInvalid(messages ...string) error

func GetDb

func GetDb(o DbOpts) (*sql.DB, error)

Gets the sql database handle for the database specified in the DriverName options parameter.

func GetRows

func GetRows(db *sql.DB, query string, lp *ListArgs, args ...interface{}) (*sql.Rows, error)

GetRows returns a *sql.Rows iterator after adding limit and offset, results are sorted by default 'updated' desc. Sql added sample: + ' ORDER by updated DESC LIMIT 20 OFFSET 1'

func LogErr

func LogErr(err error)

func Milliseconds

func Milliseconds(t time.Time) int64

func RandStringBytesMask

func RandStringBytesMask(n int64) string

RandStringBytesMask use only for dev purposes to create predictable rand passwords since the source is constant.

func RandStringBytesMaskImprSrc

func RandStringBytesMaskImprSrc(n int64) string

func Seed

func Seed(db *sql.DB, xtraSeedSql ...string) error

Seed executes sql prior to app start. Not to be exposed to client apis.

func TestStr

func TestStr(input string, rgx ...*regexp.Regexp) bool

func Tx

func Tx(db *sql.DB, txFunc func(*sql.Tx) error) (err error)

func ValidatePassword

func ValidatePassword(in string) bool

Types

type AssignRoleParams

type AssignRoleParams struct {
	Id              *int64 `json:"id"`
	Role            *Role  `json:"role"`
	CustomValidator `json:"-"`
}

func (*AssignRoleParams) Validate

func (va *AssignRoleParams) Validate() error

type ChangePasswordParams

type ChangePasswordParams struct {
	Email            string `json:"email"`
	ExistingPassword string `json:"existing_password"`
	NewPassword      string `json:"new_password"`
	ResetToken       string `json:"reset_token"`
	CustomValidator  `json:"-"`
}

func (*ChangePasswordParams) Validate

func (va *ChangePasswordParams) Validate() error

type Claims

type Claims struct {
	Role         Role  `json:"role"`
	OrgId        int64 `json:"org_id"`
	OrgSuspended bool  `json:"org_suspended"`
}

type CreateOrgParams

type CreateOrgParams struct {
	Name string  `json:"name"`
	Type OrgType `json:"type"`

	Street   string `json:"street"`
	Suburb   string `json:"suburb"`
	Town     string `json:"town"`
	Postcode string `json:"postcode"`
	Country  string `json:"country"`

	CustomValidator `json:"-"`
}

func (CreateOrgParams) Validate

func (va CreateOrgParams) Validate() error

type CustomValidator

type CustomValidator func() error

CustomValidator should be embedded in any struct which implements Validator, this allows consumers to override the validation.

func (CustomValidator) Validate

func (f CustomValidator) Validate() error

type DbOpts

type DbOpts struct {
	DriverName     string   // Optional will use sqlite3 by default.
	DataSourceName string   // Optional will use './gus.db' by default.
	Seed           bool     // Caution will regenerate schema and delete data.
	SeedSql        []string // Additional DDL or seed data.
}

type ExistsParams

type ExistsParams struct {
	Email    string `json:"email"`
	Username string `json:"username"`
}

type ListArgs

type ListArgs struct {
	Size      int     `json:"size"`      // Page size
	Page      int     `json:"page"`      // Zero-indexed page
	OrderBy   string  `json:"sort_by"`   // Order by comma separated columns
	Direction SortDir `json:"direction"` // ASC or DESC
	Deleted   bool    `json:"deleted"`   // Include deleted in results
}

func (*ListArgs) ApplyDefaults

func (p *ListArgs) ApplyDefaults()

type ListOrgsParams

type ListOrgsParams struct {
	ListArgs
	CustomValidator `json:"-"`
	OrgFilters
}

func (*ListOrgsParams) Validate

func (va *ListOrgsParams) Validate() error

type ListUsersParams

type ListUsersParams struct {
	ListArgs
	CustomValidator `json:"-"`
	UserFilters
}

func (*ListUsersParams) Validate

func (va *ListUsersParams) Validate() error

type NotAuthenticatedError

type NotAuthenticatedError struct {
}

func (*NotAuthenticatedError) Error

func (n *NotAuthenticatedError) Error() string

type NotFoundError

type NotFoundError struct {
}

func (*NotFoundError) Error

func (n *NotFoundError) Error() string

type Org

type Org struct {
	Id   int64   `json:"id"`
	Name string  `json:"name"`
	Type OrgType `json:"type"`

	Street   string `json:"street"`
	Suburb   string `json:"suburb"`
	Town     string `json:"town"`
	Postcode string `json:"postcode"`
	Country  string `json:"country"`

	Updated   int64 `json:"updated"`
	Created   int64 `json:"created"`
	Suspended bool  `json:"suspended"`
}

type OrgFilters

type OrgFilters struct {
	Name      string `schema:"name"`
	Type      int64  `schema:"type"`
	Street    string `schema:"street"` // sort by org name
	Suburb    string `schema:"suburb"`
	Town      string `schema:"town"`
	Postcode  string `schema:"postcode"`
	Suspended *bool  `schema:"suspended"`
}

type OrgListResponse

type OrgListResponse struct {
	ListArgs
	Total int64  `json:"total"`
	Items []*Org `json:"items"`
}

type OrgType

type OrgType int64

type Orgs

type Orgs struct {
	*Suspender
	// contains filtered or unexported fields
}

func NewOrgs

func NewOrgs(db *sql.DB) *Orgs

func (*Orgs) Create

func (us *Orgs) Create(p CreateOrgParams) (*Org, error)

func (*Orgs) Get

func (us *Orgs) Get(id int64) (*Org, error)

func (*Orgs) List

func (us *Orgs) List(p ListOrgsParams) (*OrgListResponse, error)

func (*Orgs) Update

func (us *Orgs) Update(p UpdateOrgParams) error

type PasswordGen

type PasswordGen func(n int64) string

type RateLimitExceededError

type RateLimitExceededError struct {
	Messages []string `json:"messages"`
}

func (*RateLimitExceededError) Error

func (rl *RateLimitExceededError) Error() string

type ResetPasswordParams

type ResetPasswordParams struct {
	Email           string `json:"email"`
	CustomValidator `json:"-"`
}

func (*ResetPasswordParams) Validate

func (va *ResetPasswordParams) Validate() error

type Role

type Role int64

type SignInParams

type SignInParams struct {
	Email           string `json:"email"`
	Username        string `json:"username"`
	Password        string `json:"password"`
	CustomValidator `json:"-"`
}

func (*SignInParams) Validate

func (va *SignInParams) Validate() error

type SignUpParams

type SignUpParams struct {
	Username        string `json:"username"`
	InviteCode      string `json:"invite_code"`
	Password        string `json:"password"`
	Email           string `json:"email"`
	FirstName       string `json:"first_name"`
	LastName        string `json:"last_name"`
	Phone           string `json:"phone"`
	OrgId           int64  `json:"org_id"`
	Role            Role   `json:"role"`
	Passive         bool   `json:"passive"`
	CustomValidator `json:"-"`
}

func (*SignUpParams) Validate

func (va *SignUpParams) Validate() error

type SortDir

type SortDir string
const (
	ErrStringNoSuchColumn         = "sql: no such column"
	DirectionAsc          SortDir = "ASC"
	DirectionDesc         SortDir = "DESC"
)

type Suspender

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

func NewSuspender

func NewSuspender(table string, db *sql.DB) *Suspender

func (*Suspender) Delete

func (su *Suspender) Delete(id int64) error

func (*Suspender) Restore

func (su *Suspender) Restore(id int64) error

func (*Suspender) Suspend

func (su *Suspender) Suspend(id int64) error

func (*Suspender) UnDelete

func (su *Suspender) UnDelete(id int64) error

type UpdateOrgParams

type UpdateOrgParams struct {
	Id              *int64  `json:"id"`
	Name            *string `json:"name"`
	Street          *string `json:"street"`
	Suburb          *string `json:"suburb"`
	Town            *string `json:"town"`
	Postcode        *string `json:"postcode"`
	Country         *string `json:"country"`
	CustomValidator `json:"-"`
}

func (*UpdateOrgParams) Validate

func (va *UpdateOrgParams) Validate() error

type UpdateUserParams

type UpdateUserParams struct {
	Id              *int64  `json:"id"`
	FirstName       *string `json:"first_name"`
	LastName        *string `json:"last_name"`
	Email           *string `json:"email"`
	Phone           *string `json:"phone"`
	CustomValidator `json:"-"`
}

func (*UpdateUserParams) Validate

func (va *UpdateUserParams) Validate() error

type User

type User struct {
	Id        int64  `json:"id"`
	Uid       string `json:"uid"`      // A universally unique id such as a uuid
	Username  string `json:"username"` // Same as email?? If not supplied.
	Email     string `json:"email"`
	FirstName string `json:"first_name"`
	LastName  string `json:"last_name"`
	Phone     string `json:"phone"`
	OrgId     int64  `json:"org_id"`
	OrgName   string `json:"org_name"`
	Updated   int64  `json:"updated"`
	Created   int64  `json:"created"`
	Role      Role   `json:"role"`
	Passive   bool   `json:"passive"`
	Suspended bool   `json:"suspended"`
}

func CheckRows

func CheckRows(u *User, e error) (*User, error)

type UserFilters

type UserFilters struct {
	OrgId     int64  `schema:"org_id"` // sort by org name
	Role      int64  `schema:"role"`
	Name      string `schema:"name"` // first name
	Email     string `schema:"email"`
	Suspended *bool  `schema:"suspended"`
	Phone     string `schema:"phone"`
}

type UserListResponse

type UserListResponse struct {
	ListArgs
	Total int64   `json:"total"`
	Items []*User `json:"items"`
}

type UserOpts

type UserOpts struct {
	AuthAttempts     int64       // Maximum amount of times a user can attempt to login with a given username.
	AuthLockDuration int64       // Seconds which the user will be locked out if MaxAuthAttempts has been exceeded.
	PassGen          PasswordGen // A function used to generate passwords and reset tokens
	// (as opposed to registered) this is the length of the generated password length.
	UsernameIsEmail  *bool // When true (default) the username is the email address. When false the username can be specified independently. In either scenario both can be used to sign in with the password.
	ResetTokenExpiry int64 // ResetTokenExpiry Seconds before token expired.
}

type UserWithClaims

type UserWithClaims struct {
	*User
	*Claims
}

type UserWithToken

type UserWithToken struct {
	User
	Token string `json:"token"`
}

type Users

type Users struct {
	*Suspender
	UserOpts
	// contains filtered or unexported fields
}

func NewUsers

func NewUsers(db *sql.DB, opt UserOpts) *Users

func (*Users) AssignRole

func (us *Users) AssignRole(p AssignRoleParams) error

func (*Users) ChangePassword

func (us *Users) ChangePassword(p ChangePasswordParams) error

func (*Users) Delete

func (us *Users) Delete(id int64) error

func (*Users) Exists

func (us *Users) Exists(p ExistsParams) (bool, error)

Exists returns true only if we know for certain that the email and username don't exists, otherwise we assume they might exist or they definitely exists if the error indicates as such.

func (*Users) Get

func (us *Users) Get(id int64) (*User, error)

func (*Users) GetByUsername

func (us *Users) GetByUsername(username string) (*UserWithClaims, string, error)

GetByUsername returns a user by username (or email) as well as a password hash.

func (*Users) List

func (us *Users) List(p ListUsersParams) (*UserListResponse, error)

func (*Users) ResetPassword

func (us *Users) ResetPassword(p ResetPasswordParams) (string, error)

func (*Users) SignIn

func (us *Users) SignIn(p SignInParams) (*UserWithClaims, error)

func (*Users) SignUp

func (us *Users) SignUp(p SignUpParams) (*User, string, error)

SignUp returns a user, random password and [error]

func (*Users) Update

func (us *Users) Update(p UpdateUserParams) error

type ValidationError

type ValidationError struct {
	Messages []string `json:"messages"`
}

func (*ValidationError) Error

func (v *ValidationError) Error() string

type Validator

type Validator interface {
	Validate() error
}

Jump to

Keyboard shortcuts

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