123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- // Package permissionsql provides a way to keeping track of users, login states and permissions.
- package permissionsql
- import (
- "github.com/xyproto/pinterface"
- "net/http"
- "strings"
- )
- // The structure that keeps track of the permissions for various path prefixes
- type Permissions struct {
- state *UserState
- adminPathPrefixes []string
- userPathPrefixes []string
- publicPathPrefixes []string
- rootIsPublic bool
- denied http.HandlerFunc
- }
- const (
- // Version number. Stable API within major version numbers.
- Version = 2.1
- )
- // Initialize a Permissions struct with all the default settings.
- // This will also connect to the database host at port 3306.
- func New() (*Permissions, error) {
- state, err := NewUserStateSimple()
- if err != nil {
- return nil, err
- }
- return NewPermissions(state), nil
- }
- // Initialize a Permissions struct with a database connection string
- func NewWithConf(connectionString string) (*Permissions, error) {
- state, err := NewUserState(connectionString, true)
- if err != nil {
- return nil, err
- }
- return NewPermissions(state), nil
- }
- // Initialize a Permissions struct with a dsn
- func NewWithDSN(connectionString string, database_name string) (*Permissions, error) {
- state, err := NewUserStateWithDSN(connectionString, database_name, true)
- if err != nil {
- return nil, err
- }
- return NewPermissions(state), nil
- }
- // Initialize a Permissions struct with the given UserState and
- // a few default paths for admin/user/public path prefixes.
- func NewPermissions(state *UserState) *Permissions {
- // default permissions
- return &Permissions{state,
- []string{"/admin"}, // admin path prefixes
- []string{"/repo", "/data"}, // user path prefixes
- []string{"/", "/login", "/register", "/favicon.ico", "/style", "/img", "/js",
- "/favicon.ico", "/robots.txt", "/sitemap_index.xml"}, // public
- true,
- PermissionDenied}
- }
- // Specify the http.HandlerFunc for when the permissions are denied
- func (perm *Permissions) SetDenyFunction(f http.HandlerFunc) {
- perm.denied = f
- }
- // Get the current http.HandlerFunc for when permissions are denied
- func (perm *Permissions) DenyFunction() http.HandlerFunc {
- return perm.denied
- }
- // Retrieve the UserState struct
- func (perm *Permissions) UserState() pinterface.IUserState {
- return perm.state
- }
- // Set everything to public
- func (perm *Permissions) Clear() {
- perm.adminPathPrefixes = []string{}
- perm.userPathPrefixes = []string{}
- }
- // Add an url path prefix that is a page for the logged in administrators
- func (perm *Permissions) AddAdminPath(prefix string) {
- perm.adminPathPrefixes = append(perm.adminPathPrefixes, prefix)
- }
- // Add an url path prefix that is a page for the logged in users
- func (perm *Permissions) AddUserPath(prefix string) {
- perm.userPathPrefixes = append(perm.userPathPrefixes, prefix)
- }
- // Add an url path prefix that is a public page
- func (perm *Permissions) AddPublicPath(prefix string) {
- perm.publicPathPrefixes = append(perm.publicPathPrefixes, prefix)
- }
- // Set all url path prefixes that are for the logged in administrator pages
- func (perm *Permissions) SetAdminPath(pathPrefixes []string) {
- perm.adminPathPrefixes = pathPrefixes
- }
- // Set all url path prefixes that are for the logged in user pages
- func (perm *Permissions) SetUserPath(pathPrefixes []string) {
- perm.userPathPrefixes = pathPrefixes
- }
- // Set all url path prefixes that are for the public pages
- func (perm *Permissions) SetPublicPath(pathPrefixes []string) {
- perm.publicPathPrefixes = pathPrefixes
- }
- // The default "permission denied" http handler.
- func PermissionDenied(w http.ResponseWriter, req *http.Request) {
- http.Error(w, "Permission denied.", http.StatusForbidden)
- }
- // Check if a given request should be rejected.
- func (perm *Permissions) Rejected(w http.ResponseWriter, req *http.Request) bool {
- reject := false
- path := req.URL.Path // the path of the url that the user wish to visit
- // If it's not "/" and set to be public regardless of permissions
- if !(perm.rootIsPublic && path == "/") {
- // Reject if it is an admin page and user does not have admin permissions
- for _, prefix := range perm.adminPathPrefixes {
- if strings.HasPrefix(path, prefix) {
- if !perm.state.AdminRights(req) {
- reject = true
- break
- }
- }
- }
- if !reject {
- // Reject if it's a user page and the user does not have user rights
- for _, prefix := range perm.userPathPrefixes {
- if strings.HasPrefix(path, prefix) {
- if !perm.state.UserRights(req) {
- reject = true
- break
- }
- }
- }
- }
- if !reject {
- // Reject if it's not a public page
- found := false
- for _, prefix := range perm.publicPathPrefixes {
- if strings.HasPrefix(path, prefix) {
- found = true
- break
- }
- }
- if !found {
- reject = true
- }
- }
- }
- return reject
- }
- // Middleware handler (compatible with Negroni)
- func (perm *Permissions) ServeHTTP(w http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
- // Check if the user has the right admin/user rights
- if perm.Rejected(w, req) {
- // Get and call the Permission Denied function
- perm.DenyFunction()(w, req)
- // Reject the request by not calling the next handler below
- return
- }
- // Call the next middleware handler
- next(w, req)
- }
|