github.com/kyleu/dbaudit@v0.0.2-0.20240321155047-ff2f2c940496/app/lib/user/permission.go (about)

     1  // Package user - Content managed by Project Forge, see [projectforge.md] for details.
     2  package user
     3  
     4  import (
     5  	"cmp"
     6  	"fmt"
     7  	"slices"
     8  	"strings"
     9  
    10  	"github.com/kyleu/dbaudit/app/util"
    11  )
    12  
    13  const permPrefix = "perm: "
    14  
    15  var (
    16  	PermissionsLogger util.Logger
    17  	perms             Permissions
    18  )
    19  
    20  func SetPermissions(allowDefault bool, ps ...*Permission) {
    21  	perms = make(Permissions, 0, len(ps)+4)
    22  	perms = append(perms, Perm("/auth", "*", true), Perm("/profile", "*", true))
    23  	perms = append(perms, ps...)
    24  	perms = append(perms, Perm("/admin", "*", false), Perm("/about", "*", true), Perm("/", "*", allowDefault))
    25  }
    26  
    27  func GetPermissions() Permissions {
    28  	ret := make(Permissions, 0, len(perms))
    29  	return append(ret, perms...)
    30  }
    31  
    32  type Permission struct {
    33  	Path  string `json:"path"`
    34  	Match string `json:"match"`
    35  	Allow bool   `json:"allow"`
    36  }
    37  
    38  func Check(path string, accounts Accounts) (bool, string) {
    39  	return perms.Check(path, accounts)
    40  }
    41  
    42  func IsAdmin(accounts Accounts) bool {
    43  	ret, _ := perms.Check("/admin", accounts)
    44  	return ret
    45  }
    46  
    47  func Perm(p string, m string, a bool) *Permission {
    48  	return &Permission{Path: p, Match: m, Allow: a}
    49  }
    50  
    51  func (p Permission) Matches(path string) bool {
    52  	return strings.HasPrefix(path, p.Path)
    53  }
    54  
    55  func (p Permission) String() string {
    56  	return fmt.Sprintf("%s [%s::%t]", p.Path, p.Match, p.Allow)
    57  }
    58  
    59  type Permissions []*Permission
    60  
    61  func (p Permissions) Sort() {
    62  	slices.SortFunc(p, func(l *Permission, r *Permission) int {
    63  		if l.Path == r.Path {
    64  			return cmp.Compare(l.Match, r.Match)
    65  		}
    66  		return cmp.Compare(l.Path, r.Path)
    67  	})
    68  }
    69  
    70  func (p Permissions) Check(path string, accounts Accounts) (bool, string) {
    71  	if PermissionsLogger != nil {
    72  		PermissionsLogger.Debugf(permPrefix+"checking [%d] permissions for [%s]", len(p), accounts.String())
    73  	}
    74  	if len(p) == 0 {
    75  		const msg = "no permissions configured"
    76  		if PermissionsLogger != nil {
    77  			PermissionsLogger.Debug(permPrefix + msg)
    78  		}
    79  		return true, msg
    80  	}
    81  	for _, perm := range p {
    82  		if perm.Matches(path) {
    83  			if accounts.Matches(perm.Match) {
    84  				msg := fmt.Sprintf("matched [%s], result [%t]", perm.Match, perm.Allow)
    85  				if PermissionsLogger != nil {
    86  					PermissionsLogger.Debug(permPrefix + msg)
    87  				}
    88  				return perm.Allow, msg
    89  			}
    90  		}
    91  	}
    92  	msg := fmt.Sprintf("no matches among [%d] permissions", len(p))
    93  	if PermissionsLogger != nil {
    94  		PermissionsLogger.Debug(permPrefix + msg)
    95  	}
    96  	return false, msg
    97  }