session

package module
v0.0.0-...-28918d2 Latest Latest
Warning

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

Go to latest
Published: Apr 16, 2022 License: MIT Imports: 17 Imported by: 1

README

A session package intended to provide some foundation which may be cusomized and implemented, perhaps useful in github.com/gin-gonic/gin middleware aside any other heuristic it can be wired to.

This package provides a secure logon session by utilizing a sqlite3 database via GORM, so easily conforms to other data-systems.


2022-04-16
UPDATED to conform to quite a few updates to GORM

  • update gorm package references (to gorm.io/driver/sqlite and gorm.io/gorm)
  • there were a few code semantic updates such as how gorm now handles logging and lack of need to .Close() a given iteration memory space(?).

LIMITATIONS

  • freshly brewed.
  • theres no Unregister function or routeHandler!

GET STARTED

See: server example.

dataset

users table: users: id name salt hash

sessions table: sessions: id userid sessid host created expires cli-key keep-alive

  • [host] value stores what is provided to the cookie name such as <appname><port>.
  • [cli-key] is provided the client IP in base64.

response handlers

current http response handlers:
/login/ /logout/ /stat/ /register/
!unregister

middleware service configs

Regular expressions are used to validate URI path for two basic heuristics. There are two "Keys" that are configured in the enum type Service, namely Service.KeySessionIsValid and Service.KeySessionIsChecked which correspond to the following regular expression input []string arrays:

  • Service.URICheck: Regular expressions supplied here will push a boolean value into gin.Context.Set(key,value) and .Get dictionary indicating wether the response is valid. A key "lookup" (ctx.Get("lookup")) value of false tells us that checking for a valid session wasn't required. If true, then the (deault) "is-valid" key will report weather or not we have a valid session.
  • Service.URIEnforce: Regular expressions supplied here will, if we have a valid session, continue to serve content. If there is no valid session then it will (by default settings) abort the httpRequest and report a simple string message.

If no regexp string(s) is supplied to Service.URICheck or Service.URIEnforce (i.e. len(x) == 0) then no checks are performed and you've just rendered this service useless ;)

Service.MatchExpHandler default:

// DefaultMatchExpHandler uses a simple regular expression to validate
// wether or not the URI session is to be validated.
func DefaultMatchExpHandler(uri, expression string) bool {
	if match, err := regexp.MatchString(expression, uri); err == nil {
		return match
	}
	return false
}

Service.URIAbortHandler default:

// DefaultURIAbortHandler is the default abort handler.
// It simply prints "authorization required" and serves "unauthorized" http
// response 401 aborting further processing.
func DefaultURIAbortHandler(ctx *gin.Context, ename string) {
	ctx.String(http.StatusUnauthorized, "authorization required")
	ctx.Abort()
}

Documentation

Overview

Package session works with GORM to enable `http.Cookie`s, `Session` and `User` capability — along with github.com/gin-gonic/gin

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CheckPassword

func CheckPassword(password string, salt []byte, hash []byte) bool

CheckPassword compares salt/password against an existing hash.

func DefaultURIAbortHandler

func DefaultURIAbortHandler(ctx *gin.Context, ename string)

DefaultURIAbortHandler is the default abort handler. It simply prints "authorization required" and serves "unauthorized" http response 401 aborting further processing.

func DefaultURIMatchHandler

func DefaultURIMatchHandler(uri, expression string) bool

DefaultURIMatchHandler uses a simple regular expression to validate wether or not the URI session is to be validated.

func EnsureTableSessions

func EnsureTableSessions()

EnsureTableSessions creates table [sessions] if not exist.

func EnsureTableUsers

func EnsureTableUsers()

EnsureTableUsers creates table [users] if not exist.

func GetHash

func GetHash(pass []byte, salt []byte) []byte

GetHash dammit.

func GetPasswordHash

func GetPasswordHash(password string, salt []byte) []byte

GetPasswordHash makes a hash from password and salt.

func NewSaltCSRNG

func NewSaltCSRNG(c int) []byte

NewSaltCSRNG CSRNG salt

func NewSaltString

func NewSaltString(c int) string

NewSaltString calls NewSaltCSRNG and converts the result to base64 string.

func OverrideCrypto

func OverrideCrypto(hashMemSize, hashTime, hashKeyLength, hashThreadCount int64)

OverrideCrypto allows you to override default hash creation settings. Set a value to -1 to persist default(s).

*note*: that defaults are `uint32` with exception to the int32 hashThreadCount is stored to. By default defaultHashThreadCount is set using runtime.NumCUP() internally.

func QueryCookieValidate

func QueryCookieValidate(cookieName string, client *gin.Context) bool

QueryCookieValidate checks against a provided salt and hash. BUT FIRST, it checks for a valid session?

- check if we have a session cookie

- if so then...

func SetCookieDestroy

func SetCookieDestroy(cli *gin.Context, name string)

SetCookieDestroy will destroy a client session by destroying the cookie. We are setting the http.Cookie.MaxAge to -1.

Note: *Like `github.com/gogonic/gin`, we are applying `url.QueryEscape` `value` stored to the cookie so be sure to UnEscape the value when retrieved.*

func SetCookieExpires

func SetCookieExpires(cli *gin.Context, name, value string, expire time.Time)

SetCookieExpires will set a cookie with our default settings.

See `CookieDefaults` in order to override default settings.

Note: *Like `github.com/gogonic/gin`, we are applying `url.QueryEscape` `value` stored to the cookie so be sure to UnEscape the value when retrieved.*

func SetCookieSessOnly

func SetCookieSessOnly(cli *gin.Context, name, value string)

SetCookieSessOnly will set a cookie with our default settings. Will expire with the browser session.

See `CookieDefaults` in order to override default settings.

Note: *Like `github.com/gogonic/gin`, we are applying `url.QueryEscape` `value` stored to the cookie so be sure to UnEscape the value when retrieved.*

func SetDataLogging

func SetDataLogging(value bool)

SetDataLogging allows you to turn on or off GORM data logging.

func SetDefaults

func SetDefaults(sys, source string, saltSize, hashKeyLen int)

SetDefaults allows a external library to set the local datasource. Set saltSize or heshKeyLen to -1 to persist default(s).

Note that the internally defined salt size is 48 while its commonly (in the wild) something <= 32 bytes.

func SetupService

func SetupService(value *Service, engine *gin.Engine, dbsys, dbsrc string, saltSize, hashSize int)

SetupService sets up session service.

Set saltSize or hashSize to -1 to persist internal defaults.

func UserGetList

func UserGetList() map[int64]User

UserGetList gets a map of all `User`s by ID.

func WrapURIExpression

func WrapURIExpression(input string) []string

WrapURIExpression splits CDF by "," and trims leading/trailing space, then prepends "^" to the string since we're "regexp" matching uri paths with strings put here ;)

Aside from that, we leave input content in tact for use in "regexp".

Remember that by the time the response hits the server, it strips content such as "/json/" of its last slash ("/json") so don't forget we can use the dollar symbol ("$") when expecting end of URI.

Types

type FormSession

type FormSession struct {
	User string
	Pass string
	Keep string
}

FormSession will collect form data.

func GetFormSession

func GetFormSession(r *http.Request) FormSession

GetFormSession gets form values from http.Request

type LogonModel

type LogonModel struct {
	Action string      `json:"action"`
	Status bool        `json:"status"`
	Detail string      `json:"detail"`
	Data   interface{} `json:"data,omitempty"`
}

LogonModel responds to a login action such as "/login/" or (perhaps) "/login-refresh/"

type Service

type Service struct {
	FormSession
	AppID               string
	Port                string
	CookieSecure        bool
	CookieHTTPOnly      bool
	KeySessionIsValid   string
	KeySessionIsChecked string
	AdvanceOnKeepYear   int
	AdvanceOnKeepMonth  int
	AdvanceOnKeepDay    int
	// supply a uri-path token such as "/json/" to check.
	// We supply a `KeySessionIsValid` for the responseHandler
	// to utilize to handle the secure content manually.
	URICheck []string
	// Unlike URICheck, we'll abort a response for any URI
	// path provided to this list if user is not logged in.
	URIEnforce      []string
	VerboseCheck    bool
	URIMatchHandler URIMatchHandler
	URIAbortHandler URIAbortHandler
}

Service is not required with exception to this little demo ;)

func DefaultService

func DefaultService() *Service

DefaultService creates/returns a default session service configuration with no URIEnforce or URICheck definitions.

This construct can be further configured, then supplied to the call to SetupService.

func (*Service) AddDate

func (s *Service) AddDate(value interface{}) time.Time

AddDate uses ServiceConf defaults to push an expiration date forward. The `value` interface can of type `time.Time` or `session.Session`. If the supplied value is not valid, we'll return a given `time.Now()`.

func (*Service) CookieDefaults

func (s *Service) CookieDefaults(cookieMonths int, httpOnly, isSecure bool)

CookieDefaults sets default cookie expire age and security.

func (*Service) SessHost

func (s *Service) SessHost() string

SessHost gets a simple string that is used in our sessions db using Configuration.appID as the root-name.

Its also used as a foundation for cookie names.

type Session

type Session struct {
	ID        int64     `gorm:"auto_increment;unique_index;primary_key;column:id"`
	UserID    int64     `gorm:"column:user_id"` // [users].[id]
	SessID    string    `gorm:"not null;column:sessid"`
	Host      string    `gorm:"column:host"` // running multiple server instance/port(s)?
	Created   time.Time `gorm:"not null;column:created"`
	Expires   time.Time `gorm:"not null;column:expires"`
	Client    string    `gorm:"not null;column:cli-key"` // .Request.RemoteAddr
	KeepAlive bool      `gorm:"column:keep-alive"`
}

Session represents users who are logged in.

func ListSessions

func ListSessions() ([]Session, int)

ListSessions returns a list of all sessions.

The method first fetches a list of User elements then reports the Sessions with user-data (name).

func QueryCookie

func QueryCookie(host string, client *gin.Context) (Session, bool)

QueryCookie looks in `sessions` table for a matching `sess_id` and returns the matching `Session` if found or an empty session. (bool) Success value tells us if a match was found.

THIS DOES NOT VALIDATE THE SESSION! IT JUST LOOKS FOR A SESSION ON THE GIVEN HOST!

If a matching session results, may be used to determine or lookup the owning User.

- Returns `false` on error (with an empty session).

- Returns `true` on success with a Session out of our database.

func (*Session) Destroy

func (s *Session) Destroy(andSave bool)

Destroy will update the `Session.Expires` date AND the `SessID` with new, EXPIRED values.

func (*Session) GetUser

func (s *Session) GetUser() (User, bool)

GetUser gets a user by the UserID stored in the Session.

func (*Session) HasSessionForUser

func (s *Session) HasSessionForUser(u *User) (bool, error)

func (*Session) IsValid

func (s *Session) IsValid() bool

IsValid returns if the session is expired. If `Session.ID` == 0, then it just returns `false`.

This is only valid for when we've looked up the session through using a client-browser-cookie prior.

func (*Session) Refresh

func (s *Session) Refresh(save bool)

Refresh will update the `Session.Expires` date AND the `SessID` with new values.

Note that this does not store a http.Cookie.

if save is true, the record is updated in [database].[sessions] table.

func (*Session) Save

func (s *Session) Save() bool

Save session data to db.

func (*Session) SetBrowserCookieFromSession

func (s *Session) SetBrowserCookieFromSession(g *gin.Context, uname, sh string)

SetBrowserCookieFromSession makes two cookies.

The first is the sessid based on the host (port/appname) which is set to expire in 6 months (by default) if KeepAlive is true otherwise will be set to expire when the browser is closed and the second contains the name of the user and is set to expire when browser is closed.

func (Session) TableName

func (Session) TableName() string

TableName Set User's table name to be `users`

type URIAbortHandler

type URIAbortHandler func(*gin.Context, string)

URIAbortHandler is used to customize how we abort a requestHandler when UriEnforce is postured to abort when a user is not logged in.

The string parameter is the regular expression or validation input that was used to URI-check in order service the abort.

type URIMatchHandler

type URIMatchHandler func(string, string) bool

URIMatchHandler is used to see if an incoming URI is one that requires a valid session.

param uri is the uri being checked.

param unsafe is the node in our array of unsafe uri-strings.

type User

type User struct {
	ID   int64  `gorm:"auto_increment;unique_index;primary_key;column:id"`
	Name string `gorm:"size:27;column:user"`
	Salt string `gorm:"size:432;column:salt"`
	Hash string `gorm:"size:432;column:hash"`
}

User structure

func (*User) ByID

func (u *User) ByID(id int64) bool

ByID gets a user by [id].

func (*User) ByName

func (u *User) ByName(name string) bool

ByName gets a user by [name]. If `u` properties are set, then those are defaulted in FirstOrInit

return true on success

func (*User) Create

func (u *User) Create(name string, pass string) int

Create attempts to create a user and returns success or failure. If a user allready exists results in failure.

Returns

func (*User) CreateSession

func (u *User) CreateSession(r interface{}, host string, keepAlive bool) (bool, Session)

CreateSession Save a session into the sessions table.

(param: `r interface{}`) is to utilize gin-gonic/gin `*gin.Context` as its suggested input interface given that we can use it to retrieve the `ClientIP()` and store that value to our database in order to validate a given user-session.

returns true on error

func (*User) Create_CheckErr

func (u *User) Create_CheckErr(name string, pass string) UserErrorConst

Create attempts to create a user and returns success or failure. If a user allready exists results in failure.

Returns

func (User) TableName

func (User) TableName() string

TableName Set User's table name to be `users`

func (*User) UserSession

func (u *User) UserSession(host string, client *gin.Context) (Session, bool)

UserSession grabs a session from sessions table matching `user_id`, `host` and `cli-key` (is the app-id used to store SessID info).

Nothing is validated, we just grab the `sessions.session` so that it can be reused and/or updated.

returns (`Session`, `success` bool)

func (*User) ValidatePassword

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

ValidatePassword checks against a provided salt and hash. we use the user's [name] to find the table-record and then validate the password.

return false on error

func (*User) ValidateSessionByUserID

func (u *User) ValidateSessionByUserID(host string, client *gin.Context) bool

ValidateSessionByUserID checks to see if a session exists in the database provided the `User.ID` of the current `User` record.

It also checks if the session is expired.

- returns `true` if the Session is valid and has not expired.

- returns `false` if `User.ID` is NOT set or the Session has expired.

type UserErrorConst

type UserErrorConst int
const (
	Perfection UserErrorConst = iota
	HasName
	LenName
	LenPass
	CheckDB UserErrorConst = -1
)

func (UserErrorConst) String

func (u UserErrorConst) String() string

Directories

Path Synopsis
examples
cli
Package main provides a CLI executable that at worked properly at some point but ATM, hasn't been tested since a number of changes had been implemented.
Package main provides a CLI executable that at worked properly at some point but ATM, hasn't been tested since a number of changes had been implemented.
srv

Jump to

Keyboard shortcuts

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