authaus

package module
v1.0.36 Latest Latest
Warning

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

Go to latest
Published: Mar 27, 2024 License: MIT Imports: 30 Imported by: 3

README

Authaus

Authaus is an authentication and authorization system written in Go.
See the documentation for more information.
Changelog at documentation

Documentation

Overview

Package Authaus is an authentication and authorization system.

Authaus brings together the following pluggable components:

Authenticator           This simply answers the question "Is this username/password valid?"
Session Database        This stores session keys and associated tokens (aka cookies).
Permit Database         This is where you store the permits (aka permissions granted).
Role Groups Database    This knows how to interpret a permit, and turn it into a list of roles.
User Store    			This is where we store user details, such as email address, contact details, name, surname etc.

Any of these five components can be swapped out, and in fact the fourth, and fifth ones (Role Groups and User Store) are entirely optional.

A typical setup is to use LDAP as an Authenticator, and Postgres as a Session, Permit, and Role Groups database.

Your session database does not need to be particularly performant, since Authaus maintains an in-process cache of session keys and their associated tokens.

Intended Usage

Authaus was NOT designed to be a "Facebook Scale" system. The target audience is a system of perhaps 100,000 users. There is nothing fundamentally limiting about the API of Authaus, but the internals certainly have not been built with millions of users in mind.

The intended usage model is this:

Authaus is intended to be embedded inside your security system, and run as a standalone HTTP service (aka a REST service). This HTTP service CAN be open to the wide world, but it's also completely OK to let it listen only to servers inside your DMZ. Authaus only gives you the skeleton and some examples of HTTP responders. It's up to you to flesh out the details of your authentication HTTP interface, and whether you'd like that to face the world, or whether it should only be accessible via other services that you control.

At startup, your services open an HTTP connection to the Authaus service. This connection will typically live for the duration of the service. For every incoming request, you peel off whatever authentication information is associated with that request. This is either a session key, or a username/password combination. Let's call it the authorization information. You then ask Authaus to tell you WHO this authorization information belongs to, as well as WHAT this authorization information allows the requester to do (ie Authentication and Authorization). Authaus responds either with a 401 (Unauthorized), 403 (Forbidden), or a 200 (OK) and a JSON object that tells you the identity of the agent submitting this request, as well the permissions that this agent posesses. It's up to your individual services to decide what to do with that information.

It should be very easy to expose Authaus over a protocol other than HTTP, since Authaus is intended to be easy to embed. The HTTP API is merely an illustrative example.

Concepts

A `Session Key` is the long random number that is typically stored as a cookie.

A `Permit` is a set of roles that has been granted to a user. Authaus knows nothing about the contents of a permit. It simply treats it as a binary blob, and when writing it to an SQL database, encodes it as base64. The interpretation of the permit is application dependent. Typically, a Permit will hold information such as "Allowed to view billing information", or "Allowed to paint your bathroom yellow". Authaus does have a built-in module called RoleGroupDB, which has its own interpretation of what a Permit is, but you do not need to use this.

A `Token` is the result of a successful authentication. It stores the identity of a user, an expiry date, and a Permit. A token will usually be retrieved by a session key. However, you can also perform a once-off authentication, which also yields you a token, which you will typically throw away when you are finished with it.

Concurrency

All public methods of the `Central` object are callable from multiple threads. Reader-Writer locks are used in all of the caching systems.

The number of concurrent connections is limited only by the limits of the Go runtime, and the performance limits that are inherent to the simple reader-writer locks used to protect shared state.

Deployment

Authaus must be deployed as a single process (which implies running on a single logical machine). The sole reason why it must run on only one process and not more, is because of the state that lives inside the various Authaus caches. Were it not for these caches, then there would be nothing preventing you from running Authaus on as many machines as necessary.

The cached state stored inside the Authaus server is:

  • Cached Session Database
  • Cached Role Group Database

If you wanted to make Authaus runnable across multiple processes, then you would need to implement a cache invalidation system for these caches.

DOS Attacks

Authaus makes no attempt to mitigate DOS attacks. The most sane approach in this domain seems to be this (http://security.stackexchange.com/questions/12101/prevent-denial-of-service-attacks-against-slow-hashing-functions).

Crypto

The password database (created via NewAuthenticationDB_SQL) stores password hashes using the scrypt key derivation system (http://www.tarsnap.com/scrypt.html).

Internally, we store our hash in a format that can later be extended, should we wish to double-hash the passwords, etc. The hash is 65 bytes and looks like this:

Bytes                1        32     32     (sum = 65 bytes)
Information       Version    Salt   Hash

The first byte of the hash is a version number of the hash. The remaining 64 bytes are the salt and the hash itself. At present, only one version is supported, which is version 1. It consists of 32 bytes of salt, and 32 bytes of scrypt'ed hash, with scrypt parameters N=256 r=8 p=1. Note that the parameter N=256 is quite low, meaning that it is possible to compute this in approximately 1 millisecond (1,000,000 nanoseconds) on a 2009-era Intel Core i7. This is a deliberate tradeoff. On the same CPU, a SHA256 hash takes about 500 nanoseconds to compute, so we are still making it 2000 times harder to brute force the passwords than an equivalent system storing only a SHA256 salted hash. This discussion is only of relevance in the event that the password table is compromised.

No cookie signing mechanism is implemented.

Cookies are not presently transmitted with Secure:true. This must change.

LDAP Authenticator

The LDAP Authenticator is extremely simple, and provides only one function: Authenticate a user against an LDAP system (often this means Active Directory, AKA a Windows Domain).

It calls the LDAP "Bind" method, and if that succeeds for the given identity/password, then the user is considered authenticated.

We take care not to allow an "anonymous bind", which many LDAP servers allow when the password is blank.

Session Database

The Session Database runs on Postgres. It stores a table of sessions, where each row contains the following information:

  • A session key (aka the cookie's "Value")
  • The identity that created that session
  • The cached permit of that identity
  • When the session expires

When a permit is altered with Authaus, then all existing sessions have their permits altered transparently. For example, imagine User X is logged in, and his administrator grants him a new permission. User X does not need to log out and log back in again in order for his new permissions to be reflected. His new permissions will be available immediately.

Similarly, if a password is changed with Authaus, then all sessions are invalidated. Do take note though, that if a password is changed through an external mechanism (such as with LDAP), then Authaus will have no way of knowing this, and will continue to serve up sessions that were authenticated with the old password. This is a problem that needs addressing.

You can limit the number of concurrent sessions per user to 1, by setting MaxActiveSessions.ConfigSessionDB to 1. This setting may only be zero or one. Zero, which is the default, means an unlimited number of concurrent sessions per user.

Session Cache

Authaus will always place your Session Database behind its own Session Cache. This session cache is a very simple single-process in-memory cache of recent sessions. The limit on the number of entries in this cache is hard-coded, and that should probably change.

Permit Database

The Permit database runs on Postgres. It stores a table of permits, which is simply a 1:1 mapping from Identity -> Permit. The Permit is just an array of bytes, which we store base64 encoded, inside a text field. This part of the system doesn't care how you interpret that blob.

Role Group Database

The Role Group Database is an entirely optional component of Authaus. The other components of Authaus (Authenticator, PermitDB, SessionDB) do not understand your Permits. To them, a Permit is simply an arbitrary array of bytes.

The Role Group Database is a component that adds a specific meaning to a permit blob. Let's see what that specific meaning looks like...

The built-in Role Group Database interprets a permit blob as a string of 32-bit integer IDs:

// A permit with 3 "role groups"
0x000000bc 0x00000001 0x000000fe

These 32-bit integer IDs refer to "role groups" inside a database table. The "role groups" table might look like this:

------------------------------------
Role Groups Table
------------------------------------
ID              Roles
0x00000001      0x00ab 0x00aa 0x0001
0x000000bc      0x00b0
0x000000fe      0x00b0 0x00bf 0x0001

The Role Group IDs use 32-bit indices, because we assume that you are not going to create more than 2^32 different role groups. The worst case we assume here is that of an automated system that creates 100,000 roles per day. Such a system would run for more than 100 years, given a 32-bit ID. These constraints are extraordinary, suggesting that we do not even need 32 bits, but could even get away with just a 16-bit group ID.

However, we expect the number of groups to be relatively small. Our aim here, arbitrary though it may be, is to fit the permit and identity into a single ethernet packet, which one can reasonably peg at 1500 bytes. 1500 / 4 = 375. We assume that no sane human administrator will assign 375 security groups to any individual. We expect the number of groups assigned to any individual to be in the range of 1 to 20. This makes 375 a gigantic buffer.

OAuth

OAuth support in Authaus is limited to a very simple scenario:

* You wish to allow your users to login using an OAuth service - thereby outsourcing the Authentication to that external service, and using it to populate the email address of your users.

OAuth was developed in order to work with Microsoft Azure Active Directory, however it should be fairly easy to extend the code to be able to handle other OAuth providers.

Inside the database are two tables related to OAuth:

oauthchallenge: The challenge table holds OAuth sessions which have been started, and which are expected to either succeed or fail within the next few minutes. The default timeout for a challenge is 5 minutes. A challenge record is usually created the moment the user clicks on the "Sign in with Microsoft" button on your site, and it tracks that authentication attempt.

oauthsession: The session table holds OAuth sessions which have successfully authenticated, and also the token that was retrieved by a successful authorization. If a token has expired, then it is refreshed and updated in-place, inside the oauthsession table.

An OAuth login follows this sequence of events:

1. User clicks on a "Signin with X" button on your login page 2. A record is created in the oauthchallenge table, with a unique ID. This ID is a secret known only to the authaus server and the OAuth server. It is used as the `state` parameter in the OAuth login mechanism. 3. The HTTP call which prompts #2 return a redirect URL (eg via an HTTP 302 response), which redirects the user's browser to the OAuth website, so that the user can either grant or refuse access. If the user refuses, or fails to login, then the login sequence ends here. 4. Upon successful authorization with the OAuth system, the OAuth website redirects the user back to your website, to a URL such as example.com/auth/oauth/finish, and you'll typically want Authaus to handle this request directly (via HttpHandlerOAuthFinish). Authaus will extract the secrets from the URL, perform any validations necessary, and then move the record from the oauthchallenge table, into the oauthsession table. While 'moving' the record over, it will also add any additional information that was provided by the successful authentication, such as the token provided by the OAuth provider. 5. Authaus makes an API call to the OAuth system, to retrieve the email address and name of the person that just logged in, using the token just received. 6. If that email address does not exist inside authuserstore, then create a new user record for this identity. 7. Log the user into Authaus, by creating a record inside authsession, for the relevant identity. Inside the authsession table, store a link to the oauthsession record, so that there is a 1:1 link from the authsession table, to the oauthsession table (ie Authaus Session to OAuth Token). 8. Return an Authaus session cookie to the browser, thereby completing the login.

Although we only use our OAuth token a single time, during login, to retrieve the user's email address and name, we retain the OAuth token, and so we maintain the ability to make other API calls on behalf of that user. This hasn't proven necessary yet, but it seems like a reasonable bit of future-proofing.

Testing

See the guidelines at the top of all_test.go for testing instructions.

Index

Constants

View Source
const (
	AuditActionAuthentication AuditActionType = "Login"
	AuditActionCreated                        = "Created"
	AuditActionUpdated                        = "Updated"
	AuditActionDeleted                        = "Deleted"
	AuditActionResetPassword                  = "Reset Password"
	AuditActionFailedLogin                    = "Failed Login"
	AuditActionUnlocked                       = "User Account Unlocked"
	AuditActionLocked                         = "User Account Locked"
)
View Source
const (
	// The value 0 was originally placed in the CreatedBy and ModifiedBy fields of a user's record,
	// when that user was created or modified by the LDAP sync process. Later, we decided to
	// formalize this, which is when UserIdLDAPMerge was born.
	// These constants are embedded inside our DB, in the CreatedBy and ModifiedBy fields of
	// a user's record, so they may not change.
	UserIdAdministrator UserId = 0
	// skip -1, because it's such a frequent "invalid" integer code
	UserIdLDAPMerge           = -2 // Created/Modified by LDAP integration
	UserIdOAuthImplicitCreate = -3 // Created implicitly by OAuth sign-in
	UserIdMSAADMerge          = -4 // Created/Modified by MSAAD integration

)
View Source
const (
	LdapConnectionModePlainText LdapConnectionMode = iota
	LdapConnectionModeSSL                          = iota
	LdapConnectionModeTLS                          = iota
)
View Source
const (
	MatchTypeNone       MatchType = 0
	MatchTypeStartsWith           = 1 << (iota - 1)
	MatchTypeEndsWith
	MatchTypeExact
	MatchTypeStandard = MatchTypeExact | MatchTypeStartsWith | MatchTypeEndsWith
)
View Source
const DefaultOAuthLoginExpirySeconds = 5 * 60
View Source
const DefaultOAuthTokenCheckIntervalSeconds = 5 * 60
View Source
const (
	OAuthProviderMSAAD = "msaad" // Microsoft Azure Active Directory
)

Variables

View Source
var (
	// NOTE: These 'base' error strings may not be prefixes of each other,
	// otherwise it violates our NewError() concept, which ensures that
	// any Authaus error starts with one of these *unique* prefixes
	ErrConnect                = errors.New("Connect failed")
	ErrUnsupported            = errors.New("Unsupported operation")
	ErrIdentityAuthNotFound   = errors.New("Identity authorization not found")
	ErrIdentityPermitNotFound = errors.New("Identity permit not found")
	ErrIdentityEmpty          = errors.New("Identity may not be empty")
	ErrIdentityExists         = errors.New("Identity already exists")
	// We should perhaps keep a consistent error, like ErrInvalidCredentials throught the app, as it can be a security risk returning InvalidPassword to a user that may be malicious
	ErrInvalidPassword      = errors.New("Invalid password")
	ErrAccountLocked        = errors.New("Account locked. Please contact your administrator")
	ErrInvalidSessionToken  = errors.New("Invalid session token")
	ErrInvalidPasswordToken = errors.New("Invalid password token")
	ErrPasswordTokenExpired = errors.New("Password token has expired")
	ErrPasswordExpired      = errors.New("Password has expired")
	ErrInvalidPastPassword  = errors.New("Invalid previously used password")
	ErrInvalidCredentials   = errors.New("Invalid Credentials") // This error was created for LDAP authentication. LDAP does not return 'identity not found' or 'invalid password' but simply invalid credentials
)
View Source
var (
	ErrHttpBasicAuth     = errors.New("HTTP Basic Authorization must be base64(identity:password)")
	ErrHttpNotAuthorized = errors.New("No authorization information")
)
View Source
var (
	ErrGroupNotExist      = errors.New("Group does not exist")
	ErrGroupExists        = errors.New("Group already exists")
	ErrGroupNameIllegal   = errors.New("Group name may not be empty, and must not have spaces on the left or right")
	ErrGroupDuplicateName = errors.New("A group with that name already exists")
	ErrPermitInvalid      = errors.New("Permit is not a sequence of 32-bit words")
)
View Source
var AuthUserTypeStrings = map[AuthUserType]string{
	UserTypeDefault: "DEFAULT",
	UserTypeLDAP:    "LDAP",
	UserTypeOAuth:   "OAUTH",
	UserTypeMSAAD:   "MSAAD",
}

Functions

func CanonicalizeIdentity

func CanonicalizeIdentity(identity string) string

CanonicalizeIdentity transforms an identity into its canonical form. What this means is that any two identities are considered equal if their canonical forms are equal. This is simply a lower-casing of the identity, so that "bob@enterprise.com" is equal to "Bob@enterprise.com". It also trims the whitespace around the identity.

func DeleteGroup

func DeleteGroup(roleDB RoleGroupDB, groupName string) error

func EncodePermit

func EncodePermit(groupIds []GroupIDU32) []byte

Encodes a list of Group IDs into a Permit

func GetMigrationVersion added in v1.0.35

func GetMigrationVersion(conx *DBConnection) (int, error)

func GetStack added in v1.0.36

func GetStack() string

func GroupIDsToNames

func GroupIDsToNames(groupIds []GroupIDU32, db RoleGroupDB, cache map[GroupIDU32]string) (name []string, e error)

GroupIDsToNames converts group IDs to names. The 'cache' parameter is used to speed up subsequent calls to this function, because this function tends to get used in loops. The function does not remove items from the cache - cache management is left to the consumer. Do not reuse the cache outside local iterative control structures or in longer running processes. In case of missing groups, the function will proceed with "best effort", but also set the error. Only the first error will be returned. On error, should the calling function decide to proceed, a null check MUST be performed on the `name` array.

func GroupNameIsLegal

func GroupNameIsLegal(name string) bool

GroupNameIsLegal asserts whether or not the name is legal

func HttpHandlerLogin

func HttpHandlerLogin(config *ConfigHTTP, central *Central, w http.ResponseWriter, r *http.Request)

HttpHandlerLogin handles the 'login' request, sending back a session token (via Set-Cookie), if authentication succeeds. You may want to use this as a template to write your own.

func HttpHandlerLogout

func HttpHandlerLogout(config *ConfigHTTP, central *Central, w http.ResponseWriter, r *http.Request)

func HttpHandlerWhoAmI

func HttpHandlerWhoAmI(config *ConfigHTTP, central *Central, w http.ResponseWriter, r *http.Request)

HttpHandlerWhoAmI handles the 'whoami' request, which is really just for debugging

func HttpNoCache added in v1.0.3

func HttpNoCache(w http.ResponseWriter)

func HttpSendHTML added in v1.0.3

func HttpSendHTML(w http.ResponseWriter, responseCode int, responseBody string)

func HttpSendJSON added in v1.0.3

func HttpSendJSON(w http.ResponseWriter, responseCode int, responseObject interface{})

func HttpSendTxt

func HttpSendTxt(w http.ResponseWriter, responseCode int, responseBody string)

func MergeLDAP added in v1.0.9

func MergeLDAP(c *Central)

func MergeLdapUsersIntoLocalUserStore added in v1.0.9

func MergeLdapUsersIntoLocalUserStore(x *Central, ldapUsers []AuthUser, imqsUsers []AuthUser)

We are reading users from LDAP/AD and merging them into the IMQS userstore

func NewError

func NewError(base error, detail string) error

NewError is to be used whenever you return an Authaus error. We rely upon the prefix of the error string to identify the broad category of the error.

func NewLDAPConnect

func NewLDAPConnect(config *ConfigLDAP) (*ldap.LDAPConnection, error)

func NewLDAPConnectAndBind

func NewLDAPConnectAndBind(config *ConfigLDAP) (*ldap.LDAPConnection, error)

func RandomString

func RandomString(nchars int, corpus string) string

RandomString returns a random string of 'nchars' bytes, sampled uniformly from the given corpus of byte characters.

func RunHttp

func RunHttp(config *ConfigHTTP, central *Central) error

Run as a standalone HTTP server. This just wires up the various HTTP handler functions and starts a listener. You will probably want to add your own entry points and do that yourself instead of using this. This function is useful for demo/example purposes.

func RunHttpFromConfig

func RunHttpFromConfig(config *Config) error

func RunMigrations

func RunMigrations(conx *DBConnection) error

func SqlCreateDatabase

func SqlCreateDatabase(conx *DBConnection) error

Types

type AuditActionType

type AuditActionType string

type Auditor

type Auditor interface {
	AuditUserAction(identity, item, context string, auditActionType AuditActionType)
}

type AuthCheck

type AuthCheck int
const (
	AuthCheckDefault         AuthCheck = 0
	AuthCheckPasswordExpired AuthCheck = 1 << (iota - 1)
)

type AuthGroup

type AuthGroup struct {
	ID       GroupIDU32     // DB-generated id
	Name     string         // Administrators need this name to keep sense of things. Example of this is "finance" or "engineering".
	PermList PermissionList // Application-defined permission bits (ie every value from 0..65535 pertains to one particular permission)
}

An Authorization Group. This stores a list of permissions.

func LoadOrCreateGroup

func LoadOrCreateGroup(roleDB RoleGroupDB, groupName string, createIfNotExist bool) (*AuthGroup, error)

func ReadRawGroups added in v1.0.15

func ReadRawGroups(importedGroups []RawAuthGroup) ([]AuthGroup, error)

func (*AuthGroup) AddPerm

func (x *AuthGroup) AddPerm(perm PermissionU16)

This is a no-op if the permission is already set

func (AuthGroup) Clone

func (x AuthGroup) Clone() *AuthGroup

func (*AuthGroup) HasPerm

func (x *AuthGroup) HasPerm(perm PermissionU16) bool

func (*AuthGroup) RemovePerm

func (x *AuthGroup) RemovePerm(perm PermissionU16)

This is a no-op if the permission is not set

type AuthUser

type AuthUser struct {
	UserId               UserId       `json:"userID"`
	Email                string       `json:"email"`
	Username             string       `json:"userName"`
	Firstname            string       `json:"firstName"`
	Lastname             string       `json:"lastName"`
	Mobilenumber         string       `json:"mobileNumber"`
	Telephonenumber      string       `json:"telephoneNumber`
	Remarks              string       `json:"remarks"`
	Created              time.Time    `json:"created"`
	CreatedBy            UserId       `json:"createdBy"`
	Modified             time.Time    `json:"modified"`
	ModifiedBy           UserId       `json:"modifiedBy"`
	Type                 AuthUserType `json:"type"`
	Archived             bool         `json:"archived"`
	InternalUUID         string       `json:"internalUUID"`
	ExternalUUID         string       `json:"externalUUID"`
	PasswordModifiedDate time.Time    `json:"passwordModifiedDate"`
	AccountLocked        bool         `json:"accountLocked"`
}

type AuthUserType

type AuthUserType int
const (
	UserTypeDefault AuthUserType = 0 // An internal Authaus user, created by an explicit create user command
	UserTypeLDAP    AuthUserType = 1 // Created via sync from an LDAP server
	UserTypeOAuth   AuthUserType = 2 // Created automatically via an OAuth login
	UserTypeMSAAD   AuthUserType = 3 // Created via sync from Microsoft Azure Active Directory
)

These constants are embedded inside our database (in the table AuthUserStore). They may never change.

func (AuthUserType) CanRenameIdentity

func (u AuthUserType) CanRenameIdentity() bool

func (AuthUserType) CanSetPassword

func (u AuthUserType) CanSetPassword() bool

type Central

type Central struct {
	// Stats must be first so that we are guaranteed to get it 8-byte aligned. We atomically
	// increment counters inside CentralStats, and the atomic functions need 8-byte alignment
	// on their operands.
	Stats                   CentralStats
	Auditor                 Auditor
	LockingPolicy           LockingPolicy
	Log                     *log.Logger
	MaxActiveSessions       int32
	NewSessionExpiresAfter  time.Duration
	DisablePasswordReuse    bool
	PasswordExpiresAfter    time.Duration
	UsersExemptFromExpiring []string
	MaxFailedLoginAttempts  int // only applies if EnableAccountLocking is true
	EnableAccountLocking    bool
	OAuth                   OAuth
	MSAAD                   MSAAD
	DB                      *sql.DB
	// contains filtered or unexported fields
}

For lack of a better name, this is the single hub of authentication that you interact with. All public methods of Central are callable from multiple threads.

func NewCentral

func NewCentral(logfile string, ldap LDAP, userStore UserStore, permitDB PermitDB, sessionDB SessionDB, roleGroupDB RoleGroupDB) *Central

Create a new Central object from the specified pieces. roleGroupDB may be nil

func NewCentralDummy

func NewCentralDummy(logfile string) *Central

func NewCentralFromConfig

func NewCentralFromConfig(config *Config) (central *Central, err error)

Create a new 'Central' object from a Config.

func (*Central) ArchiveIdentity

func (x *Central) ArchiveIdentity(userId UserId) error

Archive a user in the AuthUserStore.

func (*Central) AuthenticateUser

func (x *Central) AuthenticateUser(identity, password string, authTypeCheck AuthCheck) error

func (*Central) Close

func (x *Central) Close()

func (*Central) CreateSession added in v1.0.3

func (x *Central) CreateSession(user *AuthUser, clientIPAddress, oauthSessionID string) (sessionkey string, token *Token, err error)

CreateSession creates a new login session, after you have authenticated the caller Returns a session key, which can be used in future to retrieve the token. The internal session expiry is controlled with NewSessionExpiresAfter. The session key is typically sent to the client as a cookie. oauthSessionID is only applicable when this is an OAuth login.

func (*Central) CreateUserStoreIdentity

func (x *Central) CreateUserStoreIdentity(user *AuthUser, password string) (UserId, error)

Create an identity in the AuthUserStore.

func (*Central) ExemptFromExpiryCheck added in v1.0.34

func (x *Central) ExemptFromExpiryCheck(username string) bool

func (*Central) GetAllOAuthTokenIDs added in v1.0.35

func (x *Central) GetAllOAuthTokenIDs() ([]string, error)

func (*Central) GetAllTokens added in v1.0.35

func (x *Central) GetAllTokens(includeExpired bool) ([]*Token, error)

func (*Central) GetAuthenticatorIdentities

func (x *Central) GetAuthenticatorIdentities(getIdentitiesFlag GetIdentitiesFlag) ([]AuthUser, error)

GetAuthenticatorIdentities retrieves all identities known to the Authenticator.

func (*Central) GetPermit

func (x *Central) GetPermit(userId UserId) (*Permit, error)

GetPermit retrieves a Permit.

func (*Central) GetPermits

func (x *Central) GetPermits() (map[UserId]*Permit, error)

GetPermits retrieves all Permits.

func (*Central) GetRoleGroupDB

func (x *Central) GetRoleGroupDB() RoleGroupDB

GetRoleGroupDB retrieves the Role Group Database (which may be nil)

func (*Central) GetTokenFromIdentityPassword

func (x *Central) GetTokenFromIdentityPassword(identity, password string) (*Token, error)

Perform a once-off authentication

func (*Central) GetTokenFromSession

func (x *Central) GetTokenFromSession(sessionkey string) (*Token, error)

Pass in a session key that was generated with a call to Login(), and get back a token. A session key is typically a cookie.

func (*Central) GetUserFromIdentity

func (x *Central) GetUserFromIdentity(identity string) (AuthUser, error)

GetUserFromIdentity gets AuthUser object from either an email address or a username.

func (*Central) GetUserFromUserId

func (x *Central) GetUserFromUserId(userId UserId) (AuthUser, error)

GetUserFromUserId gets AuthUser object from userid.

func (*Central) GetUserNameFromUserId

func (x *Central) GetUserNameFromUserId(userId UserId) string

GetUserNameFromUserId gets AuthUser full name from userid.

func (*Central) InvalidateSessionsForIdentity

func (x *Central) InvalidateSessionsForIdentity(userId UserId) error

Invalidate all sessions for a particular identity

func (*Central) IsShuttingDown added in v1.0.18

func (x *Central) IsShuttingDown() bool

func (*Central) Login

func (x *Central) Login(username, password string, clientIPAddress string) (sessionkey string, token *Token, err error)

Authenticate the username + password, and if successful, call CreateSession()

func (*Central) Logout

func (x *Central) Logout(sessionkey string) error

Logout, which erases the session key

func (*Central) MergeTick

func (x *Central) MergeTick()

func (*Central) RemoveGroupFromAllUsers added in v1.0.35

func (x *Central) RemoveGroupFromAllUsers(groupIDString string) error

func (*Central) RenameIdentity

func (x *Central) RenameIdentity(oldIdent, newIdent string) error

Rename an identity. Invalidates all existing sessions for that identity

func (*Central) ResetPasswordFinish

func (x *Central) ResetPasswordFinish(userId UserId, token string, password string) error

Complete the password reset process, by providing a token that was generated by ResetPasswordStart. If this succeeds, then the password is set to 'password', and the token becomes invalid.

func (*Central) ResetPasswordStart

func (x *Central) ResetPasswordStart(userId UserId, expires time.Time) (string, error)

Create a one-time token that can be used to reset the password with a subsequent call to ResetPasswordFinish. Any subsequent call to ResetPasswordStart causes the current token to be invalidated, so there can only be a single active token. The token is valid until the time specified by 'expires'.

func (*Central) SetPassword

func (x *Central) SetPassword(userId UserId, password string) error

Change a Password. This invalidates all sessions for this identity.

func (*Central) SetPermit

func (x *Central) SetPermit(userId UserId, permit *Permit) error

Change a Permit.

func (*Central) SetSessionCacheSize

func (x *Central) SetSessionCacheSize(maxSessions int)

Set the size of the in-memory session cache

func (*Central) StartMergeTicker

func (x *Central) StartMergeTicker() error

Merges ldap with user store every merge tick

func (*Central) UnlockAccount

func (x *Central) UnlockAccount(userId UserId) error

Unlock user in the AuthUserStore.

func (*Central) UpdateIdentity

func (x *Central) UpdateIdentity(user *AuthUser) error

Update a user in the AuthUserStore.

type CentralStats

type CentralStats struct {
	InvalidSessionKeys uint64
	ExpiredSessionKeys uint64
	InvalidPasswords   uint64
	EmptyIdentities    uint64
	GoodOnceOffAuth    uint64
	GoodLogin          uint64
	Logout             uint64
	UserLoginAttempts  map[string]uint64
	// contains filtered or unexported fields
}

func (*CentralStats) IncrementAndLog

func (x *CentralStats) IncrementAndLog(name string, val *uint64, logger *log.Logger)

func (*CentralStats) IncrementEmptyIdentities

func (x *CentralStats) IncrementEmptyIdentities(logger *log.Logger)

func (*CentralStats) IncrementExpiredSessionKey

func (x *CentralStats) IncrementExpiredSessionKey(logger *log.Logger)

func (*CentralStats) IncrementGoodLogin

func (x *CentralStats) IncrementGoodLogin(logger *log.Logger)

func (*CentralStats) IncrementGoodOnceOffAuth

func (x *CentralStats) IncrementGoodOnceOffAuth(logger *log.Logger)

func (*CentralStats) IncrementInvalidPasswordHistory

func (x *CentralStats) IncrementInvalidPasswordHistory(logger *log.Logger, username string, clientIPAddress string)

func (*CentralStats) IncrementInvalidPasswords

func (x *CentralStats) IncrementInvalidPasswords(logger *log.Logger)

func (*CentralStats) IncrementInvalidSessionKey

func (x *CentralStats) IncrementInvalidSessionKey(logger *log.Logger)

func (*CentralStats) IncrementLogout

func (x *CentralStats) IncrementLogout(logger *log.Logger)

func (*CentralStats) ResetInvalidPasswordHistory

func (x *CentralStats) ResetInvalidPasswordHistory(logger *log.Logger, username string, clientIPAddress string)

type Config

type Config struct {
	DB                     DBConnection
	Log                    ConfigLog
	HTTP                   ConfigHTTP
	SessionDB              ConfigSessionDB
	LDAP                   ConfigLDAP
	UserStore              ConfigUserStoreDB
	OAuth                  ConfigOAuth
	MSAAD                  ConfigMSAAD
	AuditServiceUrl        string
	EnableAccountLocking   bool
	MaxFailedLoginAttempts int
}

Configuration information. This is typically loaded from a .json config file.

func (*Config) LoadFile

func (x *Config) LoadFile(filename string) error

func (*Config) Reset

func (x *Config) Reset()

type ConfigHTTP

type ConfigHTTP struct {
	CookieName   string
	CookieSecure bool
	Port         string
	Bind         string
}

type ConfigLDAP

type ConfigLDAP struct {
	LdapHost           string //
	LdapPort           uint16 //
	Encryption         string // "", "TLS", "SSL"
	LdapUsername       string //
	LdapPassword       string //
	LdapDomain         string //
	LdapTickerTime     int    // seconds
	BaseDN             string //
	SysAdminEmail      string //
	LdapSearchFilter   string
	InsecureSkipVerify bool // If true, then skip SSL verification. Only applicable when Encryption = SSL
	DebugUserPull      bool // If true, prints out the result of every LDAP user pull
}

type ConfigLog

type ConfigLog struct {
	Filename string
}

type ConfigMSAAD added in v1.0.9

type ConfigMSAAD struct {
	Verbose                 bool              // If true, then emit verbose logging
	DryRun                  bool              // If true, don't actually take any action, just log the intended actions
	TenantID                string            // Your tenant UUID (ie ID of your AAD instance)
	ClientID                string            // Your client UUID (ie ID of your application)
	ClientSecret            string            // Secrets used for authenticating Azure AD requests
	MergeIntervalSeconds    int               // If non-zero, then overrides the merge interval
	DefaultRoles            []string          // Roles that are activated by default if a user has any one of the AAD roles
	AutoDiscoverPermissions bool              // Experimental config to allow the AD system to attempt to pick up any IMQS related configurations. This takes precidence over explicitly specifying the RoleToGroup in conf, and will therefore not be validated against the role to group after it has been fetched
	Domain                  string            // Domain is embedded in the client configuration as a text field that one can use to scan AD role names and discover permissions. It is only relevant when AutoDiscoverPermissions is set to true
	RoleToGroup             map[string]string // Map from principleName of AAD role, to Authaus group.
	AllowArchiveUser        bool              // If true, then archive users who no longer have the relevant roles in the AAD
	PassthroughClientIDs    []string          // Client IDs of trusted IMQS apps utilising app-to-app passthrough auth
}

ConfigMSAAD is the JSON definition for the Microsoft Azure Active Directory synchronization settings

type ConfigOAuth added in v1.0.3

type ConfigOAuth struct {
	Providers                 map[string]*ConfigOAuthProvider
	Verbose                   bool   // If true, then print a lot of debugging information
	ForceFastTokenRefresh     bool   // If true, then force a token refresh every 120 seconds. This is for testing the token refresh code.
	LoginExpirySeconds        int64  // A session that starts must be completed within this time period (eg 5 minutes)
	TokenCheckIntervalSeconds int    // Override interval at which we check that OAuth tokens are still valid, and if not, invalidate the Authaus session. Set to -1 to disable this check.
	DefaultProvider           string // Can be set to the name of one of the Providers. This was created for the login JS front-end, to act as though the user has pressed the "Sign-in with XYZ" button as soon as the page is loaded.
}

func (*ConfigOAuth) LoginExpiry added in v1.0.3

func (c *ConfigOAuth) LoginExpiry() time.Duration

type ConfigOAuthProvider added in v1.0.3

type ConfigOAuthProvider struct {
	Type            string // See OAuthProvider___ constants for legal values
	Title           string // Name of provider that user sees (probably need an image too)
	ClientID        string // For MSAAD
	LoginURL        string // eg https://login.microsoftonline.com/e1ff61b3-a3da-4639-ae31-c6dff3ce7bfb/oauth2/v2.0/authorize
	TokenURL        string // eg https://login.microsoftonline.com/e1ff61b3-a3da-4639-ae31-c6dff3ce7bfb/oauth2/v2.0/token
	RedirectURL     string // eg https://stellenbosch.imqs.co.za/auth2/oauth/finish. URL must be listed in IMQS app in Azure. Can be http://localhost/auth2/oauth/finish for testing.
	Scope           string
	ClientSecret    string
	AllowCreateUser bool // If true, then automatically create an Authaus user for an OAuth user, if the user succeeds in logging in
}

type ConfigSessionDB

type ConfigSessionDB struct {
	MaxActiveSessions    int32 // Maximum number of active sessions per user. legal values are 0 and 1. Zero means unlimited.
	SessionExpirySeconds int64 // Lifetime of newly created sessions, in seconds. Zero means default, which is defaultSessionExpirySeconds (30 days)
}

type ConfigUserStoreDB

type ConfigUserStoreDB struct {
	DisablePasswordReuse    bool
	OldPasswordHistorySize  int // When DisablePasswordReuse is true, this is how far back in history we look (i.e. number of password changes), to determine if a password has been used before
	PasswordExpirySeconds   int
	UsersExemptFromExpiring []string // List of users that are not subject to password expiry. Username will be used for comparison.
}

type DBConnection

type DBConnection struct {
	Driver   string
	Host     string
	Port     uint16
	Database string
	User     string
	Password string
	SSL      bool
}

Database connection information

func (*DBConnection) Connect

func (x *DBConnection) Connect() (*sql.DB, error)

func (*DBConnection) ConnectionString

func (x *DBConnection) ConnectionString() string

func (*DBConnection) Equals

func (x *DBConnection) Equals(y *DBConnection) bool

type GetIdentitiesFlag

type GetIdentitiesFlag int
const (
	GetIdentitiesFlagNone    GetIdentitiesFlag = 0
	GetIdentitiesFlagDeleted GetIdentitiesFlag = 1 << (iota - 1)
)

type GroupIDU32

type GroupIDU32 uint32

GroupIDU32 is our group IDs are unsigned 32-bit integers

func GroupNamesToIDs

func GroupNamesToIDs(groups []string, db RoleGroupDB) ([]GroupIDU32, error)

Converts group names to group IDs. From here you can use EncodePermit to get a blob that is ready for use as Permit.Roles

type GroupIDU32s added in v1.0.11

type GroupIDU32s []GroupIDU32

GroupIDU32s is a containing the group IDs

func DecodePermit

func DecodePermit(permit []byte) (GroupIDU32s, error)

DecodePermit decodes a Permit into a list of Group IDs

func (*GroupIDU32s) ContainsIndex added in v1.0.11

func (gid *GroupIDU32s) ContainsIndex(idx GroupIDU32) bool

ContainsIndex returns whether or not the requested index is contained in the group

func (*GroupIDU32s) IndexOf added in v1.0.11

func (gid *GroupIDU32s) IndexOf(idx GroupIDU32) int

IndexOf returns the index of the group

type LDAP

type LDAP interface {
	Authenticate(identity, password string) error // Return nil if the password is correct, otherwise one of ErrIdentityAuthNotFound or ErrInvalidPassword
	GetLdapUsers() ([]AuthUser, error)            // Retrieve the list of users from ldap
	Close()                                       // Typically used to close a database handle
}

The LDAP interface allows authentication and the ability to retrieve the LDAP's users and merge them into our system

type LdapConnectionMode

type LdapConnectionMode int

type LdapImpl

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

func NewAuthenticator_LDAP

func NewAuthenticator_LDAP(config *ConfigLDAP) *LdapImpl

func (*LdapImpl) Authenticate

func (x *LdapImpl) Authenticate(identity, password string) error

func (*LdapImpl) Close

func (x *LdapImpl) Close()

func (*LdapImpl) GetLdapUsers

func (x *LdapImpl) GetLdapUsers() ([]AuthUser, error)

type LockingPolicy

type LockingPolicy interface {
	IsLockable(identity string) (bool, error)
}

LockingPolicy controls on a per-user basis, whether that user's account is automatically locked, after a set number of failed login attempts. It was created to disable the locking of special accounts, such as administrators or internal infrastructure accounts. This only applies if EnableAccountLocking is true

type MSAAD added in v1.0.9

type MSAAD struct {
	Config ConfigMSAAD
	// contains filtered or unexported fields
}

MSAAD is a container for the Microsoft Azure Active Directory synchronization system

func (*MSAAD) Initialize added in v1.0.9

func (m *MSAAD) Initialize(parent *Central)

Initialize seeks to initialize the parent context on the MSAAD object

func (*MSAAD) SynchronizeUsers added in v1.0.9

func (m *MSAAD) SynchronizeUsers() error

SynchronizeUsers rebuilds the role groups cache, as well as re-fetches the users from MSAAD, for the purpose of bringing IMQS' internal roledb cache and postgres database up to date

type MatchType added in v1.0.11

type MatchType int

MatchType attempts to give more information about the type of match that was detected

func Match added in v1.0.11

func Match(lhs, rhs string) MatchType

Match attempts to provide a descr

type OAuth added in v1.0.3

type OAuth struct {
	Config ConfigOAuth
	// contains filtered or unexported fields
}

OAuth This is just a container for OAuth functions and state, so that we don't pollute the 'Central' struct with all of our stuff.

func (*OAuth) HttpHandlerOAuthFinish added in v1.0.3

func (x *OAuth) HttpHandlerOAuthFinish(w http.ResponseWriter, r *http.Request)

HttpHandlerOAuthFinish This is a dummy "finish" handler. A real handler would be a function where you create a session cookie, and then redirect the user to a reasonable landing page.

func (*OAuth) HttpHandlerOAuthStart added in v1.0.3

func (x *OAuth) HttpHandlerOAuthStart(w http.ResponseWriter, r *http.Request)

HttpHandlerOAuthStart This is a GET or POST request that the frontend calls, in order to start an OAuth login sequence

func (*OAuth) HttpHandlerOAuthTest added in v1.0.3

func (x *OAuth) HttpHandlerOAuthTest(w http.ResponseWriter, r *http.Request)

HttpHandlerOAuthTest It's useful to keep a function like this around, so that you can iterate on this stuff without having to go through a complete login cycle every time. I'm afraid I'm going to burn up some max-logins-per-hour quota or something like that.

func (*OAuth) Initialize added in v1.0.3

func (x *OAuth) Initialize(parent *Central)

func (*OAuth) OAuthFinish added in v1.0.3

func (x *OAuth) OAuthFinish(r *http.Request) (*OAuthCompletedResult, error)

OAuthFinish handles the URL where the user gets redirected after completing a successful login to the OAuth provider. It is the OAuth provider's website that redirects the user back here. This is a GET request, and inside the URL, behind the fragment, are the login details. One major thing omitted from this function, is the creation of a session record, and returning a cookie to the browser. This is intentional, because it's very likely that you may want to do additional things, such as assigning some roles, before creating the session. If AllowCreateUser is false, and the user does not exist in the Authaus database, then this function does not return an error. However, the UserId field in OAuthCompletedResult will be zero, and it is your responsibility to deal with that however you choose (either create a user yourself, or halt the login process). Note that if you do not create an Authaus session within a few minutes, then the OAuth session will be cleared out by the cleaner thread, which deletes stale OAuth sessions, which have no link to an Authaus session.

func (*OAuth) OAuthLoginUsernamePassword added in v1.0.22

func (x *OAuth) OAuthLoginUsernamePassword(username string, password string) (error, string)

OAuthLoginUsernamePassword Provides support for username and password login to MSAAD App to app authentication. This is NOT recommended for normal user access but for trusted applications that have their own user credentials in MSAAD AND is linked to the same tenant ID under which auth operates.

type OAuthCompletedResult added in v1.0.3

type OAuthCompletedResult struct {
	Profile        *OAuthUserProfile // User profile that has been read from the OAuth server
	IsNewUser      bool              // True if this user has just been created
	UserId         UserId            // Non-zero if Success is true
	OAuthSessionID string
}

type OAuthUserProfile added in v1.0.9

type OAuthUserProfile struct {
	DisplayName string
	FirstName   string
	LastName    string
	Email       string
	Phone       string
	UUID        string
}

type PasswordEnforcement

type PasswordEnforcement int
const (
	PasswordEnforcementDefault PasswordEnforcement = 0
	PasswordEnforcementReuse   PasswordEnforcement = 1 << (iota - 1)
)

type PermissionList

type PermissionList []PermissionU16

A list of permissions

func PermitResolveToList

func PermitResolveToList(permit []byte, db RoleGroupDB) (PermissionList, error)

This goes from Permit -> Groups -> PermList Permit has 0..n Groups Group has 0..n PermList We produce a list of all unique PermList that appear in any of the groups inside this permit. You can think of this as a binary OR operation. In case of missing groups, the function will proceed with "best effort", but also set the error. Only the first error will be returned.

func (*PermissionList) Add

func (x *PermissionList) Add(perm PermissionU16)

Add adds this permission to the list. Takes no action if the permission is already present.

func (*PermissionList) Diff added in v1.0.21

func (PermissionList) Has

func (x PermissionList) Has(perm PermissionU16) bool

Has returns true if the list contains this permission

func (*PermissionList) Remove

func (x *PermissionList) Remove(perm PermissionU16)

Remove removes this permission from the lst Takes no action if the permission is not present.

type PermissionNameTable

type PermissionNameTable map[PermissionU16]string

A mapping from 16-bit permission number to a textual name of that permission

func (*PermissionNameTable) Inverted

func (x *PermissionNameTable) Inverted() map[string]PermissionU16

Produces a map from permission name to permission number

type PermissionU16

type PermissionU16 uint16

Any permission in the system is uniquely described by a 16-bit unsigned integer

type Permit

type Permit struct {
	Roles []byte
}

A Permit is an opaque binary string that encodes domain-specific roles. This could be a string of bits with special meanings, or a blob of JSON, etc.

func (*Permit) Clone

func (p *Permit) Clone() *Permit

Clone returns a copy of the permit

func (*Permit) Deserialize

func (p *Permit) Deserialize(encoded string) error

Deserialize decodes the encoded parameter and stores it in the native permit struct

func (*Permit) Equals

func (p *Permit) Equals(b *Permit) bool

Equals compares whether or not the roles in the resepective permits are equal

func (*Permit) Serialize

func (p *Permit) Serialize() string

Serialize returns a base64 representation of the permit

func (*Permit) ToString added in v1.0.11

func (p *Permit) ToString() string

ToString puts out the string representation of the permit - only intended to be used as a speedy debug helper

type PermitDB

type PermitDB interface {
	GetPermit(userId UserId) (*Permit, error) // Retrieve a permit
	GetPermits() (map[UserId]*Permit, error)  // Retrieve all permits as a map from identity to the permit.
	// This should create the permit if it does not exist. A call to this function is
	// followed by a call to SessionDB.PermitChanged. identity is canonicalized before being stored
	SetPermit(userId UserId, permit *Permit) error
	Close() // Typically used to close a database handle
}

A Permit database performs no validation. It simply returns the Permit owned by a particular user. All operations except for Close must be thread-safe.

func NewPermitDB_SQL

func NewPermitDB_SQL(db *sql.DB) (PermitDB, error)

type RawAuthGroup added in v1.0.15

type RawAuthGroup struct {
	ID       GroupIDU32
	Name     string
	PermList string
}

type RoleGroupCache

type RoleGroupCache struct {
	// contains filtered or unexported fields
}
Role Group cache

This caches all role groups from the backend database. We assume that this database will never be particularly large, so we simply allow our cache to grow indefinitely. All public functions are thread-safe.

func (*RoleGroupCache) Close

func (x *RoleGroupCache) Close()

func (*RoleGroupCache) DeleteGroup

func (x *RoleGroupCache) DeleteGroup(group *AuthGroup) (err error)

func (*RoleGroupCache) GetByID

func (x *RoleGroupCache) GetByID(id GroupIDU32) (*AuthGroup, error)

func (*RoleGroupCache) GetByName

func (x *RoleGroupCache) GetByName(name string) (*AuthGroup, error)

func (*RoleGroupCache) GetGroups

func (x *RoleGroupCache) GetGroups() ([]*AuthGroup, error)

func (*RoleGroupCache) GetGroupsRaw added in v1.0.15

func (x *RoleGroupCache) GetGroupsRaw() ([]RawAuthGroup, error)

GetGroupsRaw's results are not cached. The point is to get the current state of the db for the export

func (*RoleGroupCache) InsertGroup

func (x *RoleGroupCache) InsertGroup(group *AuthGroup) (err error)

func (*RoleGroupCache) UpdateGroup

func (x *RoleGroupCache) UpdateGroup(group *AuthGroup) (err error)

type RoleGroupDB

type RoleGroupDB interface {
	GetGroups() ([]*AuthGroup, error)
	GetGroupsRaw() ([]RawAuthGroup, error)
	GetByName(name string) (*AuthGroup, error)
	GetByID(id GroupIDU32) (*AuthGroup, error)
	InsertGroup(group *AuthGroup) error
	DeleteGroup(group *AuthGroup) error
	UpdateGroup(group *AuthGroup) error
	Close()
}

A Role Group database stores a list of Groups. Each Group has a list of permissions that it enables.

func NewCachedRoleGroupDB

func NewCachedRoleGroupDB(backend RoleGroupDB) RoleGroupDB

Create a new RoleGroupDB that transparently caches reads of groups

func NewRoleGroupDB_SQL

func NewRoleGroupDB_SQL(db *sql.DB) (RoleGroupDB, error)

type SessionDB

type SessionDB interface {
	Write(sessionkey string, token *Token) error       // Set a token
	Read(sessionkey string) (*Token, error)            // Fetch a token
	Delete(sessionkey string) error                    // Delete a token (used to implement "logout")
	PermitChanged(userId UserId, permit *Permit) error // Assign the new permit to all of the sessions belonging to 'identity'
	InvalidateSessionsForIdentity(userId UserId) error // Delete all sessions belonging to the given identity. This is called after a password has been changed, or an identity renamed.
	GetAllTokens(includeExpired bool) ([]*Token, error)
	GetAllOAuthTokenIDs() ([]string, error)
	Close() // Typically used to close a database handle
}

A Session database is essentially a key/value store where the keys are session tokens, and the values are tuples of (Identity,Permit). All operations except for Close must be thread-safe.

func NewSessionDB_SQL

func NewSessionDB_SQL(db *sql.DB) (SessionDB, error)

type Token

type Token struct {
	Identity       string
	UserId         UserId
	Email          string
	Username       string
	InternalUUID   string
	Expires        time.Time
	Permit         Permit
	OAuthSessionID string // Only applicable if this login occurred via OAuth
}

Token is the result of a successful authentication request. It contains everything that we know about this authentication event, which includes the identity that performed the request, when this token expires, and the permit belonging to this identity.

func HttpHandlerBasicAuth

func HttpHandlerBasicAuth(central *Central, r *http.Request) (*Token, error)

func HttpHandlerPrelude

func HttpHandlerPrelude(config *ConfigHTTP, central *Central, r *http.Request) (*Token, error)

HttpHandlerPrelude reads the session cookie or the HTTP "Basic" Authorization header to determine whether this request is authorized.

func HttpHandlerPreludeWithError

func HttpHandlerPreludeWithError(config *ConfigHTTP, central *Central, w http.ResponseWriter, r *http.Request) (*Token, error)

Runs the Prelude function, but before returning an error, sends an appropriate error response to the HTTP ResponseWriter. If this function returns a non-nil error, then it means that you should not send anything else to the http response.

type UserId

type UserId int64
const (
	// The number of session records that are stored in the in-process cache
	DefaultSessionCacheSize        = 10000
	NullUserId              UserId = 0
)

type UserStore

type UserStore interface {
	Authenticate(identity, password string, authTypeCheck AuthCheck) error                                        // Return nil error if the username and password are correct, otherwise one of ErrIdentityAuthNotFound or ErrInvalidPassword
	SetPassword(userId UserId, password string, enforceTypeCheck PasswordEnforcement) error                       // This sets the password to a user account
	SetConfig(passwordExpiry time.Duration, oldPasswordHistorySize int, usersExemptFromExpiring []string) error   // If any parameter is zero, then it is ignored
	ResetPasswordStart(userId UserId, expires time.Time) (string, error)                                          // Create a one-time token that can be used to reset the password with a subsequent call to ResetPasswordFinish
	ResetPasswordFinish(userId UserId, token string, password string, enforceTypeCheck PasswordEnforcement) error // Check that token matches the last one generated by ResetPasswordStart, and if so, call SetPassword
	CreateIdentity(user *AuthUser, password string) (UserId, error)                                               // Create a new identity. If the identity already exists, then this must return ErrIdentityExists.
	UpdateIdentity(user *AuthUser) error                                                                          // Update an identity. Change email address or name etc.
	ArchiveIdentity(userId UserId) error                                                                          // Archive an identity
	// TODO RenameIdentity was deprecated in May 2016, replaced by UpdateIdentity. We need to remove this once PCS team has made the necessary updates
	RenameIdentity(oldIdent, newIdent string) error                        // Rename an identity. Returns ErrIdentityAuthNotFound if oldIdent does not exist. Returns ErrIdentityExists if newIdent already exists.
	GetUserFromIdentity(identity string) (*AuthUser, error)                // Gets the user object from the identity supplied
	LockAccount(userId UserId) error                                       // Locks an account
	UnlockAccount(userId UserId) error                                     // Unlocks an account
	GetUserFromUserId(userId UserId) (*AuthUser, error)                    // Gets the user object from the userId supplied
	GetIdentities(getIdentitiesFlag GetIdentitiesFlag) ([]AuthUser, error) // Retrieve a list of all identities
	Close()                                                                // Typically used to close a database handle
}

The primary job of the UserStore, is to store and authenticate users. It is also responsible for adding new users, changing passwords etc. All operations except for Close must be thread-safe.

func NewUserStoreDB_SQL

func NewUserStoreDB_SQL(db *sql.DB) (UserStore, error)

Jump to

Keyboard shortcuts

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