permissionsql.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. // Package permissionsql provides a way to keeping track of users, login states and permissions.
  2. package permissionsql
  3. import (
  4. "github.com/xyproto/pinterface"
  5. "net/http"
  6. "strings"
  7. )
  8. // The structure that keeps track of the permissions for various path prefixes
  9. type Permissions struct {
  10. state *UserState
  11. adminPathPrefixes []string
  12. userPathPrefixes []string
  13. publicPathPrefixes []string
  14. rootIsPublic bool
  15. denied http.HandlerFunc
  16. }
  17. const (
  18. // Version number. Stable API within major version numbers.
  19. Version = 2.1
  20. )
  21. // Initialize a Permissions struct with all the default settings.
  22. // This will also connect to the database host at port 3306.
  23. func New() (*Permissions, error) {
  24. state, err := NewUserStateSimple()
  25. if err != nil {
  26. return nil, err
  27. }
  28. return NewPermissions(state), nil
  29. }
  30. // Initialize a Permissions struct with a database connection string
  31. func NewWithConf(connectionString string) (*Permissions, error) {
  32. state, err := NewUserState(connectionString, true)
  33. if err != nil {
  34. return nil, err
  35. }
  36. return NewPermissions(state), nil
  37. }
  38. // Initialize a Permissions struct with a dsn
  39. func NewWithDSN(connectionString string, database_name string) (*Permissions, error) {
  40. state, err := NewUserStateWithDSN(connectionString, database_name, true)
  41. if err != nil {
  42. return nil, err
  43. }
  44. return NewPermissions(state), nil
  45. }
  46. // Initialize a Permissions struct with the given UserState and
  47. // a few default paths for admin/user/public path prefixes.
  48. func NewPermissions(state *UserState) *Permissions {
  49. // default permissions
  50. return &Permissions{state,
  51. []string{"/admin"}, // admin path prefixes
  52. []string{"/repo", "/data"}, // user path prefixes
  53. []string{"/", "/login", "/register", "/favicon.ico", "/style", "/img", "/js",
  54. "/favicon.ico", "/robots.txt", "/sitemap_index.xml"}, // public
  55. true,
  56. PermissionDenied}
  57. }
  58. // Specify the http.HandlerFunc for when the permissions are denied
  59. func (perm *Permissions) SetDenyFunction(f http.HandlerFunc) {
  60. perm.denied = f
  61. }
  62. // Get the current http.HandlerFunc for when permissions are denied
  63. func (perm *Permissions) DenyFunction() http.HandlerFunc {
  64. return perm.denied
  65. }
  66. // Retrieve the UserState struct
  67. func (perm *Permissions) UserState() pinterface.IUserState {
  68. return perm.state
  69. }
  70. // Set everything to public
  71. func (perm *Permissions) Clear() {
  72. perm.adminPathPrefixes = []string{}
  73. perm.userPathPrefixes = []string{}
  74. }
  75. // Add an url path prefix that is a page for the logged in administrators
  76. func (perm *Permissions) AddAdminPath(prefix string) {
  77. perm.adminPathPrefixes = append(perm.adminPathPrefixes, prefix)
  78. }
  79. // Add an url path prefix that is a page for the logged in users
  80. func (perm *Permissions) AddUserPath(prefix string) {
  81. perm.userPathPrefixes = append(perm.userPathPrefixes, prefix)
  82. }
  83. // Add an url path prefix that is a public page
  84. func (perm *Permissions) AddPublicPath(prefix string) {
  85. perm.publicPathPrefixes = append(perm.publicPathPrefixes, prefix)
  86. }
  87. // Set all url path prefixes that are for the logged in administrator pages
  88. func (perm *Permissions) SetAdminPath(pathPrefixes []string) {
  89. perm.adminPathPrefixes = pathPrefixes
  90. }
  91. // Set all url path prefixes that are for the logged in user pages
  92. func (perm *Permissions) SetUserPath(pathPrefixes []string) {
  93. perm.userPathPrefixes = pathPrefixes
  94. }
  95. // Set all url path prefixes that are for the public pages
  96. func (perm *Permissions) SetPublicPath(pathPrefixes []string) {
  97. perm.publicPathPrefixes = pathPrefixes
  98. }
  99. // The default "permission denied" http handler.
  100. func PermissionDenied(w http.ResponseWriter, req *http.Request) {
  101. http.Error(w, "Permission denied.", http.StatusForbidden)
  102. }
  103. // Check if a given request should be rejected.
  104. func (perm *Permissions) Rejected(w http.ResponseWriter, req *http.Request) bool {
  105. reject := false
  106. path := req.URL.Path // the path of the url that the user wish to visit
  107. // If it's not "/" and set to be public regardless of permissions
  108. if !(perm.rootIsPublic && path == "/") {
  109. // Reject if it is an admin page and user does not have admin permissions
  110. for _, prefix := range perm.adminPathPrefixes {
  111. if strings.HasPrefix(path, prefix) {
  112. if !perm.state.AdminRights(req) {
  113. reject = true
  114. break
  115. }
  116. }
  117. }
  118. if !reject {
  119. // Reject if it's a user page and the user does not have user rights
  120. for _, prefix := range perm.userPathPrefixes {
  121. if strings.HasPrefix(path, prefix) {
  122. if !perm.state.UserRights(req) {
  123. reject = true
  124. break
  125. }
  126. }
  127. }
  128. }
  129. if !reject {
  130. // Reject if it's not a public page
  131. found := false
  132. for _, prefix := range perm.publicPathPrefixes {
  133. if strings.HasPrefix(path, prefix) {
  134. found = true
  135. break
  136. }
  137. }
  138. if !found {
  139. reject = true
  140. }
  141. }
  142. }
  143. return reject
  144. }
  145. // Middleware handler (compatible with Negroni)
  146. func (perm *Permissions) ServeHTTP(w http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
  147. // Check if the user has the right admin/user rights
  148. if perm.Rejected(w, req) {
  149. // Get and call the Permission Denied function
  150. perm.DenyFunction()(w, req)
  151. // Reject the request by not calling the next handler below
  152. return
  153. }
  154. // Call the next middleware handler
  155. next(w, req)
  156. }