gw

package module
v0.0.0-...-b2072f1 Latest Latest
Warning

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

Go to latest
Published: Jul 29, 2019 License: MIT Imports: 36 Imported by: 0

README

GW

A Go Web application framework. This folds reusable parts of our infrastructure at Zepheira into one common base. It provides:

  • LDAP account integration
  • JSON Web Token middleware (using a JWT library and a JWT middleware library)
  • Authentication middleware based on LDAP and JWT
  • CORS middleware
  • Settings interface to configure LDAP and JWT
  • Centralized error handling
  • Template convenience methods
  • Database convenience methods

We try to keep the API as stable as possible.

Usage

Add as an import.

import (
    "github.com/zepheira/gw"
)
LDAP

Tested with OpenLDAP. In our setup, we have:

  • an administrators LDAP group that automatically gets admin access in every application, which configurable but generally assumed to exist
  • a forgotten password reset LDAP user with sufficient authority to change user passwords
  • a binding LDAP user
  • the ppolicy overlay enabled, which can lock out users after password failures
  • put the special case lock time 000001010000Z set by ADMIN_LOCKED_TIME to use to distinguish password failures from intentional locks
  • the memberof overlay to provide inferred user-group property values
  • users in their own DN subtree, using uid for the identifier
  • groups in their own DN subtree, using cn for the identifier

Review the LDAPGroup and LDAPUser structs for interacting with the provided methods.

JWT

Review the libraries listed above. Private and public RSA keys need to be generated and their file locations handed to the SetupJWTMiddleware method.

Tying into LDAP, the JWT claims made are username and exp for expiration date.

CORS

The CORS settings allow for a list of domains (origins) to be passed in. Any request either matching a listed origin exactly or being a subdomain of an origin will have the CORS HTTP headers added, only for OPTIONS, GET, and POST methods.

Templates

Templates will be loaded from a specified directory. They'll be put in a map[string]*template.Template where the string index is the relative path to the template file. Only one level of subdirectories are read in. Middleware is provided to map requests to templates.

Delimiters for template fields are replaced with << and >> instead of the usual double curly brackets to avoid collisions with other templating systems (like AngularJS).

To facilitate testing, templates can be reloaded by sending the application SIGUSR1, i.e., kill -SIGUSR1 <pid>.

Settings

In order to pass your configuration values into this library, write up an implementation of the Settings interface, which will allow the rest of the library to call GetCORSOptions, for instance, and utilize your provided list allowed origins.

Database Convenience

These methods are Postgres-specific.

There is a Migrate method using the migrate library to handle automigrating on application startup.

The QuerySetHelpder method takes values, often from HTTP requests, and appends ordering and limiting clauses to queries (which should not contain those clauses in already).

Testing

Run with

% go test -v -cover .

The views/ directory and its contents exist for testing purposes.

More tests could be written. There isn't much in the way of testing LDAP; those methods have been exercised through real world usage but are difficult to check without an active LDAP server to test against.

Releases

This framework is mirrored to GitHub from our private repository. Only squashed commits for each tag are published.

Documentation

Index

Constants

View Source
const (
	ASC  = "ascend"
	DESC = "descend"
)
View Source
const (
	ADMIN_LOCKED_TIME = "000001010000Z"
)

Variables

This section is empty.

Functions

func AddMember

func AddMember(ls []LDAPConfiguration, group *LDAPGroup, user *LDAPUser, creds *LDAPCredentials) error

func AddOwner

func AddOwner(ls []LDAPConfiguration, group *LDAPGroup, user *LDAPUser, creds *LDAPCredentials) error

func AllowedOriginMethod

func AllowedOriginMethod(opts *CORSOptions, origin, method string) bool

func AuthHandlersRegister

func AuthHandlersRegister(router *mux.Router, c Settings)

func Authenticate

func Authenticate(ls []LDAPConfiguration, user, pass string) (bool, error)

func CreateGroup

func CreateGroup(ls []LDAPConfiguration, group *LDAPGroup, creds *LDAPCredentials) error

func CreateUser

func CreateUser(ls []LDAPConfiguration, user *LDAPUser, password string, creds *LDAPCredentials) error

func DisableUser

func DisableUser(ls []LDAPConfiguration, user *LDAPUser, creds *LDAPCredentials, reason string) error

func EditGroup

func EditGroup(ls []LDAPConfiguration, group *LDAPGroup, creds *LDAPCredentials) error

func EditUser

func EditUser(ls []LDAPConfiguration, user *LDAPUser, creds *LDAPCredentials) error

func EnableUser

func EnableUser(ls []LDAPConfiguration, user *LDAPUser, creds *LDAPCredentials) error

func IsMember

func IsMember(ls []LDAPConfiguration, groupname, username string) (bool, error)

func ListenForReload

func ListenForReload(c Settings, dir string)

func LoadTemplates

func LoadTemplates(directory string) (map[string]*template.Template, error)

func Migrate

func Migrate(connection, path string) error

func NotAuth

func NotAuth(token *jwt.Token) bool

func RemoveMember

func RemoveMember(ls []LDAPConfiguration, group *LDAPGroup, user *LDAPUser, creds *LDAPCredentials) error

func RemoveOwner

func RemoveOwner(ls []LDAPConfiguration, group *LDAPGroup, user *LDAPUser, creds *LDAPCredentials) error

func SetForgotPassword

func SetForgotPassword(ls []LDAPConfiguration, user *LDAPUser, newPass string) error

func SetPassword

func SetPassword(ls []LDAPConfiguration, user *LDAPUser, oldPass, newPass string) error

func SetupJWTMiddleware

func SetupJWTMiddleware(private, public string) (*jwtmiddleware.JWTMiddleware, *rsa.PrivateKey, string, error)

func SynchronizeAll

func SynchronizeAll(ls []LDAPConfiguration, r SyncReceiver, c chan error)

func TemplatesRegister

func TemplatesRegister(router *mux.Router, c Settings)

func UsernameFromToken

func UsernameFromToken(token *jwt.Token) string

Types

type AppError

type AppError struct {
	Code  int
	Error error
}

Code is HTTP status code, Error the Go error describing the problem, possibly for re-display to the user.

func LoginHandler

func LoginHandler(w http.ResponseWriter, r *http.Request) *AppError

Takes login info and returns a JWT if successful, 403 if not

func LogoutHandler

func LogoutHandler(w http.ResponseWriter, r *http.Request) *AppError

Does nothing for now

func ParseJSON

func ParseJSON(data interface{}, query string) *AppError

func ReceiveJSON

func ReceiveJSON(data interface{}, r *http.Request) *AppError

func RefreshHandler

func RefreshHandler(w http.ResponseWriter, r *http.Request) *AppError

Takes an existing, valid token and issues a newer, fresher expiration for it.

func SendJSON

func SendJSON(data interface{}, w http.ResponseWriter, r *http.Request) *AppError

func TemplateHandler

func TemplateHandler(w http.ResponseWriter, r *http.Request, template string) *AppError

type AppHandler

type AppHandler func(http.ResponseWriter, *http.Request) *AppError

func Auth

func Auth(fn AppHandler) AppHandler

Middleware to mark a resource as one requiring authentication to access

func CORS

func CORS(fn AppHandler) AppHandler

func Middleware

func Middleware(fn AppHandler, c interface{}) AppHandler

Always conclude by wrapping handlers in Middleware. If there are exceptions to this rule, it should be because no level requires context. All other wrappers depend on it; for instance, AuthRequired needs the context to determine logged-in status.

func PartialAuth

func PartialAuth(fn AppHandler) AppHandler

Middleware to mark a resource as granting greater access when authenticated, but authentication not required for limited access.

func TemplateMiddleware

func TemplateMiddleware(tmpl string) AppHandler

func (AppHandler) ServeHTTP

func (fn AppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)

type CORSOptions

type CORSOptions struct {
	Origins []string `json:"origins"`
}

type ErrorInfo

type ErrorInfo struct {
	Code  int    `json:"code"`
	Error string `json:"error"`
	Field string `json:"field,omitempty"`
}

type Events

type Events interface {
	OnLogin(*LDAPUser) error
}

type LDAP

type LDAP struct {
	Host           string
	Port           uint
	BindUsername   string
	BindPassword   string
	ForgotUsername string
	ForgotPassword string
	AdminGroup     string
	BaseDN         string
	UserDN         string
	GroupDN        string
}

func (LDAP) AddMember

func (l LDAP) AddMember(group *LDAPGroup, user *LDAPUser, creds *LDAPCredentials) error

func (LDAP) AddOwner

func (l LDAP) AddOwner(group *LDAPGroup, user *LDAPUser, creds *LDAPCredentials) error

func (LDAP) Auth

func (l LDAP) Auth(user, pass string) (bool, error)

func (LDAP) CreateGroup

func (l LDAP) CreateGroup(group *LDAPGroup, creds *LDAPCredentials) error

func (LDAP) CreateUser

func (l LDAP) CreateUser(user *LDAPUser, password string, creds *LDAPCredentials) error

func (LDAP) DNToGroupname

func (l LDAP) DNToGroupname(dn string) string

func (LDAP) DNToUsername

func (l LDAP) DNToUsername(dn string) string

func (LDAP) DisableUser

func (l LDAP) DisableUser(user *LDAPUser, creds *LDAPCredentials, reason string) error

func (LDAP) EditGroup

func (l LDAP) EditGroup(group *LDAPGroup, creds *LDAPCredentials) error

func (LDAP) EditUser

func (l LDAP) EditUser(user *LDAPUser, creds *LDAPCredentials) error

func (LDAP) EnableUser

func (l LDAP) EnableUser(user *LDAPUser, creds *LDAPCredentials) error

func (LDAP) FormatServer

func (l LDAP) FormatServer() string

func (LDAP) GetAdminGroup

func (l LDAP) GetAdminGroup() string

func (LDAP) GetBaseDN

func (l LDAP) GetBaseDN() string

func (LDAP) GetBindPassword

func (l LDAP) GetBindPassword() string

func (LDAP) GetBindUsername

func (l LDAP) GetBindUsername() string

func (LDAP) GetForgotPassword

func (l LDAP) GetForgotPassword() string

func (LDAP) GetForgotUsername

func (l LDAP) GetForgotUsername() string

func (LDAP) GetGroup

func (l LDAP) GetGroup(groupname string) (*LDAPGroup, error)

func (LDAP) GetGroupDN

func (l LDAP) GetGroupDN() string

func (LDAP) GetHost

func (l LDAP) GetHost() string

func (LDAP) GetPort

func (l LDAP) GetPort() uint

func (LDAP) GetUser

func (l LDAP) GetUser(username string) (*LDAPUser, error)

func (LDAP) GetUserDN

func (l LDAP) GetUserDN() string

func (LDAP) GroupnameToDN

func (l LDAP) GroupnameToDN(groupname string) string

func (LDAP) IsMember

func (l LDAP) IsMember(groupname, username string) (bool, error)

func (LDAP) RemoveMember

func (l LDAP) RemoveMember(group *LDAPGroup, user *LDAPUser, creds *LDAPCredentials) error

func (LDAP) RemoveOwner

func (l LDAP) RemoveOwner(group *LDAPGroup, user *LDAPUser, creds *LDAPCredentials) error

func (LDAP) SetForgotPassword

func (l LDAP) SetForgotPassword(user *LDAPUser, newPass string) error

func (LDAP) SetPassword

func (l LDAP) SetPassword(user *LDAPUser, oldPass, newPass string) error

func (LDAP) Setup

func (l LDAP) Setup(bind bool) (*ldap.Conn, error)

func (LDAP) SynchronizeAll

func (l LDAP) SynchronizeAll(r SyncReceiver) error

func (LDAP) UsernameToDN

func (l LDAP) UsernameToDN(username string) string

type LDAPConfiguration

type LDAPConfiguration interface {
	GetHost() string
	GetPort() uint
	GetBindUsername() string
	GetBindPassword() string
	GetForgotUsername() string
	GetForgotPassword() string
	GetAdminGroup() string
	GetBaseDN() string
	GetUserDN() string
	GetGroupDN() string
	UsernameToDN(string) string
	GroupnameToDN(string) string
	DNToUsername(string) string
	DNToGroupname(string) string
	FormatServer() string
	Auth(string, string) (bool, error)
	GetUser(string) (*LDAPUser, error)
	GetGroup(string) (*LDAPGroup, error)
	CreateUser(*LDAPUser, string, *LDAPCredentials) error
	EditUser(*LDAPUser, *LDAPCredentials) error
	EnableUser(*LDAPUser, *LDAPCredentials) error
	DisableUser(*LDAPUser, *LDAPCredentials, string) error
	IsMember(string, string) (bool, error)
	CreateGroup(*LDAPGroup, *LDAPCredentials) error
	EditGroup(*LDAPGroup, *LDAPCredentials) error
	AddMember(*LDAPGroup, *LDAPUser, *LDAPCredentials) error
	RemoveMember(*LDAPGroup, *LDAPUser, *LDAPCredentials) error
	AddOwner(*LDAPGroup, *LDAPUser, *LDAPCredentials) error
	RemoveOwner(*LDAPGroup, *LDAPUser, *LDAPCredentials) error
	SetPassword(*LDAPUser, string, string) error
	SetForgotPassword(*LDAPUser, string) error
	SynchronizeAll(SyncReceiver) error
}

type LDAPCredentials

type LDAPCredentials struct {
	DN       string
	Password string
}

type LDAPGroup

type LDAPGroup struct {
	DN        string
	Groupname string
	Name      string
	Owners    []string
	Members   []string
}

func GetGroup

func GetGroup(ls []LDAPConfiguration, groupname string) (*LDAPGroup, error)

type LDAPUser

type LDAPUser struct {
	DN         string
	Username   string
	FirstName  string
	LastName   string
	Email      string
	IsActive   bool
	IsAdmin    bool
	LockedTime string
	Groups     []string
}

func GetUser

func GetUser(ls []LDAPConfiguration, username string) (*LDAPUser, error)

type MockSettings

type MockSettings struct {
}

func (MockSettings) GetCORSOptions

func (s MockSettings) GetCORSOptions() *CORSOptions

func (MockSettings) GetEvents

func (s MockSettings) GetEvents() Events

func (MockSettings) GetJWT

func (s MockSettings) GetJWT() *jwtmiddleware.JWTMiddleware

func (MockSettings) GetJWTPublicKey

func (s MockSettings) GetJWTPublicKey() string

func (MockSettings) GetJWTSigningKey

func (s MockSettings) GetJWTSigningKey() *rsa.PrivateKey

func (MockSettings) GetLDAP

func (s MockSettings) GetLDAP() []LDAPConfiguration

func (MockSettings) GetPool

func (s MockSettings) GetPool() *bpool.BufferPool

func (MockSettings) GetTemplates

func (s MockSettings) GetTemplates() map[string]*template.Template

func (MockSettings) SetTemplates

func (s MockSettings) SetTemplates(map[string]*template.Template)

type QuerySettings

type QuerySettings struct {
	Start    int
	Count    int
	Field    string
	Order    string
	Compiled string
}

func QuerySetHelper

func QuerySetHelper(args url.Values, col, ord string) (*QuerySettings, error)

Supports ordering and subsetting of queries by building the relevant portions of a SELECT query out of URL query args. Returns a string with the relevant clauses, generally suitable for appending to the end of a query (without said clauses).

  • start: integer, starting index of query. Defaults to 0.
  • count: integer, max size of query. Defaults to 0, which is unlimited. This will probably change!
  • field: string, name of column to sort by. Default supplied as col arg to function. Leave arg empty to ignore sorting.
  • order: ("ascend"|"descend") Default supplied as ord arg to function.

type Settings

type Settings interface {
	GetLDAP() []LDAPConfiguration
	SetTemplates(map[string]*template.Template)
	GetTemplates() map[string]*template.Template
	GetPool() *bpool.BufferPool
	GetJWT() *jwtmiddleware.JWTMiddleware
	GetJWTSigningKey() *rsa.PrivateKey
	GetJWTPublicKey() string
	GetEvents() Events
	GetCORSOptions() *CORSOptions
}

type SyncReceiver

type SyncReceiver interface {
	OnUser(*LDAPUser) error
	OnGroup(*LDAPGroup) error
}

type User

type User struct {
	Username string `json:"username"`
	Password string `json:"password"`
}

Jump to

Keyboard shortcuts

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