permissions

package module
v0.0.0-...-0fee66c Latest Latest
Warning

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

Go to latest
Published: Oct 8, 2018 License: MIT Imports: 18 Imported by: 1

README

Deprecated, please use permissions2 instead!


Permissions Build Status GoDoc

Middleware for keeping track of users, login states and permissions.

Online API Documentation

godoc.org

Versions

Features and limitations

  • Uses secure cookies and stores user information in a Redis database.
  • Suitable for running a local Redis server, registering/confirming users and managing public/user/admin pages.
  • Also supports connecting to remote Redis servers.
  • Supports registration and confirmation via generated confirmation codes.
  • Tries to keep things simple.
  • Only supports "public", "user" and "admin" permissions out of the box, but offers functionality for implementing more fine grained permissions, if so desired.
  • Can be used together with Martini.
  • Also works together with Gin.
  • Should also work with other web-frameworks, since the standard http.HandlerFunc is used everywhere.
  • The default permissions can be cleared with the Clear() function.

Example for Negroni

package main

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/codegangsta/negroni"
	"github.com/xyproto/permissions"
)

func main() {
	n := negroni.Classic()
	mux := http.NewServeMux()

	// New permissions middleware
	perm := permissions.New()

	// Blank slate, no default permissions
	//perm.Clear()

	// Get the userstate, used in the handlers below
	userstate := perm.UserState()

	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "Has user bob: %v\n", userstate.HasUser("bob"))
		fmt.Fprintf(w, "Logged in on server: %v\n", userstate.IsLoggedIn("bob"))
		fmt.Fprintf(w, "Is confirmed: %v\n", userstate.IsConfirmed("bob"))
		fmt.Fprintf(w, "Username stored in cookies (or blank): %v\n", userstate.GetUsername(req))
		fmt.Fprintf(w, "Current user is logged in, has a valid cookie and *user rights*: %v\n", userstate.UserRights(req))
		fmt.Fprintf(w, "Current user is logged in, has a valid cookie and *admin rights*: %v\n", userstate.AdminRights(req))
		fmt.Fprintf(w, "\nTry: /register, /confirm, /remove, /login, /logout, /data, /makeadmin and /admin")
	})

	mux.HandleFunc("/register", func(w http.ResponseWriter, req *http.Request) {
		userstate.AddUser("bob", "hunter1", "bob@zombo.com")
		fmt.Fprintf(w, "User bob was created: %v\n", userstate.HasUser("bob"))
	})

	mux.HandleFunc("/confirm", func(w http.ResponseWriter, req *http.Request) {
		userstate.MarkConfirmed("bob")
		fmt.Fprintf(w, "User bob was confirmed: %v\n", userstate.IsConfirmed("bob"))
	})

	mux.HandleFunc("/remove", func(w http.ResponseWriter, req *http.Request) {
		userstate.RemoveUser("bob")
		fmt.Fprintf(w, "User bob was removed: %v\n", !userstate.HasUser("bob"))
	})

	mux.HandleFunc("/login", func(w http.ResponseWriter, req *http.Request) {
		userstate.Login(w, "bob")
		fmt.Fprintf(w, "bob is now logged in: %v\n", userstate.IsLoggedIn("bob"))
	})

	mux.HandleFunc("/logout", func(w http.ResponseWriter, req *http.Request) {
		userstate.Logout("bob")
		fmt.Fprintf(w, "bob is now logged out: %v\n", !userstate.IsLoggedIn("bob"))
	})

	mux.HandleFunc("/makeadmin", func(w http.ResponseWriter, req *http.Request) {
		userstate.SetAdminStatus("bob")
		fmt.Fprintf(w, "bob is now administrator: %v\n", userstate.IsAdmin("bob"))
	})

	mux.HandleFunc("/data", func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "user page that only logged in users must see!")
	})

	mux.HandleFunc("/admin", func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "super secret information that only logged in administrators must see!\n\n")
		if usernames, err := userstate.GetAllUsernames(); err == nil {
			fmt.Fprintf(w, "list of all users: "+strings.Join(usernames, ", "))
		}
	})

	// Custom handler for when permissions are denied
	perm.SetDenyFunction(func(w http.ResponseWriter, req *http.Request) {
		http.Error(w, "Permission denied!", http.StatusForbidden)
	})

	// Enable the permissions middleware
	n.Use(perm)

	// Use mux for routing, this goes last
	n.UseHandler(mux)

	// Serve
	n.Run(":3000")
}

Example for Martini

package main

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/go-martini/martini"
	"github.com/xyproto/permissions"
)

func main() {
	m := martini.Classic()

	// New permissions middleware
	perm := permissions.New()

	// Blank slate, no default permissions
	//perm.Clear()

	// Get the userstate, used in the handlers below
	userstate := perm.UserState()

	m.Get("/", func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "Has user bob: %v\n", userstate.HasUser("bob"))
		fmt.Fprintf(w, "Logged in on server: %v\n", userstate.IsLoggedIn("bob"))
		fmt.Fprintf(w, "Is confirmed: %v\n", userstate.IsConfirmed("bob"))
		fmt.Fprintf(w, "Username stored in cookies (or blank): %v\n", userstate.GetUsername(req))
		fmt.Fprintf(w, "Current user is logged in, has a valid cookie and *user rights*: %v\n", userstate.UserRights(req))
		fmt.Fprintf(w, "Current user is logged in, has a valid cookie and *admin rights*: %v\n", userstate.AdminRights(req))
		fmt.Fprintf(w, "\nTry: /register, /confirm, /remove, /login, /logout, /data, /makeadmin and /admin")
	})

	m.Get("/register", func(w http.ResponseWriter, req *http.Request) {
		userstate.AddUser("bob", "hunter1", "bob@zombo.com")
		fmt.Fprintf(w, "User bob was created: %v\n", userstate.HasUser("bob"))
	})

	m.Get("/confirm", func(w http.ResponseWriter, req *http.Request) {
		userstate.MarkConfirmed("bob")
		fmt.Fprintf(w, "User bob was confirmed: %v\n", userstate.IsConfirmed("bob"))
	})

	m.Get("/remove", func(w http.ResponseWriter, req *http.Request) {
		userstate.RemoveUser("bob")
		fmt.Fprintf(w, "User bob was removed: %v\n", !userstate.HasUser("bob"))
	})

	m.Get("/login", func(w http.ResponseWriter, req *http.Request) {
		userstate.Login(w, "bob")
		fmt.Fprintf(w, "bob is now logged in: %v\n", userstate.IsLoggedIn("bob"))
	})

	m.Get("/logout", func(w http.ResponseWriter, req *http.Request) {
		userstate.Logout("bob")
		fmt.Fprintf(w, "bob is now logged out: %v\n", !userstate.IsLoggedIn("bob"))
	})

	m.Get("/makeadmin", func(w http.ResponseWriter, req *http.Request) {
		userstate.SetAdminStatus("bob")
		fmt.Fprintf(w, "bob is now administrator: %v\n", userstate.IsAdmin("bob"))
	})

	m.Get("/data", func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "user page that only logged in users must see!")
	})

	m.Get("/admin", func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, "super secret information that only logged in administrators must see!\n\n")
		if usernames, err := userstate.GetAllUsernames(); err == nil {
			fmt.Fprintf(w, "list of all users: "+strings.Join(usernames, ", "))
		}
	})

	// Set up a middleware handler for Martini, with a custom "permission denied" message.
	// Use the xyproto/fizz middleware for a simpler solution.
	permissionHandler := func(w http.ResponseWriter, req *http.Request, c martini.Context) {
		// Check if the user has the right admin/user rights
		if perm.Rejected(w, req) {
			// Deny the request
			http.Error(w, "Permission denied!", http.StatusForbidden)
			// Reject the request by not calling the next handler below
			return
		}
		// Call the next middleware handler
		c.Next()
	}

	// Enable the permissions middleware
	m.Use(permissionHandler)

	// Serve
	m.Run()
}

Default permissions

  • The /admin path prefix has admin rights by default.
  • These path prefixes has user rights by default: /repo and /data
  • These path prefixes are public by default: /, /login, /register, /style, /img, /js, /favicon.ico, /robots.txt and /sitemap_index.xml

Password hashing

  • "sha256" is used by default for hashing passwords. Use permissions2 for bcrypt.

General information

  • Version: 1.0
  • License: MIT
  • Alexander F Rødseth

Documentation

Index

Constants

View Source
const (
	// Version number. Stable API within major version numbers.
	Version = 1.0
)

Variables

This section is empty.

Functions

func GetSecureCookie

func GetSecureCookie(req *http.Request, name string, cookieSecret string) (string, bool)

func PermissionDenied

func PermissionDenied(w http.ResponseWriter, req *http.Request)

The default "permission denied" http handler.

func RandomCookieFriendlyString

func RandomCookieFriendlyString(length int) string

Generate a random, but cookie-friendly, string of the given length.

func RandomHumanFriendlyString

func RandomHumanFriendlyString(length int) string

Generate a random, but human-friendly, string of the given length. Should be possible to read out loud and send in an email without problems.

func RandomString

func RandomString(length int) string

Generate a random string of the given length.

func SetCookiePath

func SetCookiePath(w http.ResponseWriter, name, value string, age int64, path string)

Set a cookie with an explicit path. Duration is the cookie time-to-live in seconds (0 = forever).

func SetHeader

func SetHeader(w http.ResponseWriter, hdr, val string, unique bool)

Used for setting cookies

func SetSecureCookiePath

func SetSecureCookiePath(w http.ResponseWriter, name, val string, age int64, path string, cookieSecret string)

func ValidUsernamePassword

func ValidUsernamePassword(username, password string) error

Check that the given username and password are different. Also check if the chosen username only contains letters, numbers and/or underscore.

Types

type Permissions

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

func New

func New() *Permissions

Initialize a Permissions struct with all the default settings. This will also connect to the redis host at localhost:6379.

func NewPermissions

func NewPermissions(state *UserState) *Permissions

Initialize a Permissions struct with the given UserState and a few default paths for admin/user/public path prefixes.

func NewWithRedisConf

func NewWithRedisConf(dbindex int, hostPort string) *Permissions

Initialize a Permissions struct with Redis DB index and host:port

func (*Permissions) AddAdminPath

func (perm *Permissions) AddAdminPath(prefix string)

Add an url path prefix that is a page for the logged in administrators

func (*Permissions) AddPublicPath

func (perm *Permissions) AddPublicPath(prefix string)

Add an url path prefix that is a public page

func (*Permissions) AddUserPath

func (perm *Permissions) AddUserPath(prefix string)

Add an url path prefix that is a page for the logged in users

func (*Permissions) Clear

func (perm *Permissions) Clear()

Set everything to public

func (*Permissions) GetDenyFunction

func (perm *Permissions) GetDenyFunction() http.HandlerFunc

Get the current http.HandlerFunc for when permissions are denied

func (*Permissions) Rejected

func (perm *Permissions) Rejected(w http.ResponseWriter, req *http.Request) bool

Check if a given request should be rejected.

func (*Permissions) ServeHTTP

func (perm *Permissions) ServeHTTP(w http.ResponseWriter, req *http.Request, next http.HandlerFunc)

Negroni middleware handler

func (*Permissions) SetAdminPath

func (perm *Permissions) SetAdminPath(pathPrefixes []string)

Set all url path prefixes that are for the logged in administrator pages

func (*Permissions) SetDenyFunction

func (perm *Permissions) SetDenyFunction(f http.HandlerFunc)

Specify the http.HandlerFunc for when the permissions are denied

func (*Permissions) SetPublicPath

func (perm *Permissions) SetPublicPath(pathPrefixes []string)

Set all url path prefixes that are for the public pages

func (*Permissions) SetUserPath

func (perm *Permissions) SetUserPath(pathPrefixes []string)

Set all url path prefixes that are for the logged in user pages

func (*Permissions) UserState

func (perm *Permissions) UserState() *UserState

Retrieve the UserState struct

type UserState

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

func NewUserState

func NewUserState(dbindex int, randomseed bool, redisHostPort string) *UserState

Create a new *UserState that can be used for managing users. dbindex is the Redis database index. If randomseed is true, the random number generator will be seeded after generating the cookie secret. redisHostPort is host:port for the desired Redis server (can be blank for localhost) Also creates a new ConnectionPool.

func NewUserStateSimple

func NewUserStateSimple() *UserState

Create a new *UserState that can be used for managing users. The random number generator will be seeded after generating the cookie secret. A connection pool for the local Redis server (dbindex 0) will be created.

func (*UserState) AddUnconfirmed

func (state *UserState) AddUnconfirmed(username, confirmationCode string)

Add a user that is registered but not confirmed

func (*UserState) AddUser

func (state *UserState) AddUser(username, password, email string)

Creates a user and hashes the password, does not check for rights. The given data must be valid.

func (*UserState) AdminRights

func (state *UserState) AdminRights(req *http.Request) bool

Check if the current user is logged in as Administrator right now

func (*UserState) AlreadyHasConfirmationCode

func (state *UserState) AlreadyHasConfirmationCode(confirmationCode string) bool

Goes through all the confirmationCodes of all the unconfirmed users and checks if this confirmationCode already is in use

func (*UserState) Close

func (state *UserState) Close()

Close the Redis connection pool

func (*UserState) Confirm

func (state *UserState) Confirm(username string)

Both remove the username from the list of unconfirmed users and mark the user as confirmed

func (*UserState) ConfirmUserByConfirmationCode

func (state *UserState) ConfirmUserByConfirmationCode(confirmationcode string) error

Take a confirmation code and mark the corresponding unconfirmed user as confirmed

func (*UserState) CorrectPassword

func (state *UserState) CorrectPassword(username, password string) bool

Check if a password is correct. username is needed because it is part of the hash.

func (*UserState) FindUserByConfirmationCode

func (state *UserState) FindUserByConfirmationCode(confirmationcode string) (string, error)

Given a unique confirmation code, find the corresponding username

func (*UserState) GenerateUniqueConfirmationCode

func (state *UserState) GenerateUniqueConfirmationCode() (string, error)

Generate a unique confirmation code that can be used for confirming users

func (*UserState) GetAllUnconfirmedUsernames

func (state *UserState) GetAllUnconfirmedUsernames() ([]string, error)

Get all registered users that are not yet confirmed

func (*UserState) GetAllUsernames

func (state *UserState) GetAllUsernames() ([]string, error)

Get a list of all usernames

func (*UserState) GetBooleanField

func (state *UserState) GetBooleanField(username, fieldname string) bool

Return the boolean value for a given username and fieldname. If the user or field is missing, false will be returned. Useful for states where it makes sense that the returned value is not true unless everything is in order.

func (*UserState) GetConfirmationCode

func (state *UserState) GetConfirmationCode(username string) (string, error)

Get the confirmation code for a specific user

func (*UserState) GetCookieTimeout

func (state *UserState) GetCookieTimeout(username string) int64

Get how long a login cookie should last, in seconds

func (*UserState) GetDatabaseIndex

func (state *UserState) GetDatabaseIndex() int

Get the Redis database index

func (*UserState) GetEmail

func (state *UserState) GetEmail(username string) (string, error)

Get the email for the given username

func (*UserState) GetPasswordAlgo

func (state *UserState) GetPasswordAlgo() string

Get current password algorithm

func (*UserState) GetPasswordHash

func (state *UserState) GetPasswordHash(username string) (string, error)

Get the password hash for the given username

func (*UserState) GetPool

func (state *UserState) GetPool() *simpleredis.ConnectionPool

Get the Redis connection pool

func (*UserState) GetUsername

func (state *UserState) GetUsername(req *http.Request) string

Convenience function that will return a username (from the browser cookie) or an empty string

func (*UserState) GetUsernameCookie

func (state *UserState) GetUsernameCookie(req *http.Request) (string, error)

Retrieve the username that is stored in a cookie in the browser, if available

func (*UserState) GetUsers

func (state *UserState) GetUsers() *simpleredis.HashMap

Get the users HashMap

func (*UserState) HasUser

func (state *UserState) HasUser(username string) bool

Check if the given username exists

func (*UserState) HashPassword

func (state *UserState) HashPassword(username, password string) string

Hash the password if using sha256, use the cookieSecret and username as part of the salt if using bcrypt, rely on it for salting

func (*UserState) IsAdmin

func (state *UserState) IsAdmin(username string) bool

Check if the given username is an administrator

func (*UserState) IsConfirmed

func (state *UserState) IsConfirmed(username string) bool

Check if the given username is confirmed

func (*UserState) IsLoggedIn

func (state *UserState) IsLoggedIn(username string) bool

Checks if the given username is logged in or not

func (*UserState) Login

func (state *UserState) Login(w http.ResponseWriter, username string)

Convenience function for logging the user in and storing the username in a cookie

func (*UserState) Logout

func (state *UserState) Logout(username string)

Convenience function for logging the user out

func (*UserState) MarkConfirmed

func (state *UserState) MarkConfirmed(username string)

Mark a user as confirmed

func (*UserState) RemoveAdminStatus

func (state *UserState) RemoveAdminStatus(username string)

Mark user as a regular user

func (*UserState) RemoveUnconfirmed

func (state *UserState) RemoveUnconfirmed(username string)

Remove a user that is registered but not confirmed

func (*UserState) RemoveUser

func (state *UserState) RemoveUser(username string)

Remove user and login status

func (*UserState) SetAdminStatus

func (state *UserState) SetAdminStatus(username string)

Mark user as an administrator

func (*UserState) SetBooleanField

func (state *UserState) SetBooleanField(username, fieldname string, val bool)

Attempt to store a boolean value for the given username and fieldname.

func (*UserState) SetCookieTimeout

func (state *UserState) SetCookieTimeout(cookieTime int64)

Set how long a login cookie should last, in seconds

func (*UserState) SetLoggedIn

func (state *UserState) SetLoggedIn(username string)

Mark the user as logged in. Use the Login function instead, unless cookies are not involved.

func (*UserState) SetLoggedOut

func (state *UserState) SetLoggedOut(username string)

Mark the user as logged out

func (*UserState) SetMinimumConfirmationCodeLength

func (state *UserState) SetMinimumConfirmationCodeLength(length int)

Set the minimum length of the user confirmation code. The default is 20.

func (*UserState) SetPasswordAlgo

func (state *UserState) SetPasswordAlgo(algo string)

Set password algorithm

func (*UserState) SetUsernameCookie

func (state *UserState) SetUsernameCookie(w http.ResponseWriter, username string) error

Store the given username in a cookie in the browser, if possible. The user must exist.

func (*UserState) UserRights

func (state *UserState) UserRights(req *http.Request) bool

Check if the current user is logged in as a user right now

Directories

Path Synopsis
examples
gin

Jump to

Keyboard shortcuts

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