Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setup basic access control framework #64

Merged
merged 15 commits into from
Jul 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions internal/auth/permission.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package auth

import (
"net/http"

"github.com/source-academy/stories-backend/internal/auth/permissions"
)

func CheckPermissions(r *http.Request, requestedActionPermissions ...permissions.PermissionGroup) (bool, error) {
requiredPermissions := permissions.AllOf{
Groups: requestedActionPermissions,
}
return requiredPermissions.IsAuthorized(r), nil
}
20 changes: 20 additions & 0 deletions internal/auth/permissions/users/permissions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package userpermissions

type Permission string

const (
CanCreateUsers Permission = "can_create_users"
CanReadUsers Permission = "can_read_users"
CanUpdateUsers Permission = "can_update_users"
CanDeleteUsers Permission = "can_delete_users"

CanCreateGroups Permission = "can_create_groups"
CanReadGroups Permission = "can_read_groups"
CanUpdateGroups Permission = "can_update_groups"
CanDeleteGroups Permission = "can_delete_groups"

CanCreateStories Permission = "can_create_stories"
CanReadStories Permission = "can_read_stories"
CanUpdateStories Permission = "can_update_stories"
CanDeleteStories Permission = "can_delete_stories"
)
25 changes: 25 additions & 0 deletions internal/auth/permissions/users/role.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package userpermissions

import (
"net/http"

"github.com/source-academy/stories-backend/internal/auth"
groupenums "github.com/source-academy/stories-backend/internal/enums/groups"
)

type RolePermission struct {
Permission Permission
Role groupenums.Role
}

func (p RolePermission) IsAuthorized(r *http.Request) bool {
userID, err := auth.GetUserIDFrom(r)
if err != nil {
return false
}
// FIXME: Implement. Blocked by request context missing group info.
_ = userID
// role := getRole(userID, groupID)
// return groupenums.IsRoleGreaterThan(role, p.Role)
return false
}
48 changes: 48 additions & 0 deletions internal/auth/permissions/users/users.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package userpermissions

import (
"errors"

groupenums "github.com/source-academy/stories-backend/internal/enums/groups"
)

// Gets the RolePermission from a Permission. To be called
// by external packages.
// TODO: Investigate possibility of defining roles on a
// per-user/group basis in the DB.
func GetRolePermission(p Permission) *RolePermission {
switch p {
case
// Permissions for all users
CanCreateStories,
CanReadStories:
return &RolePermission{
Permission: p,
Role: groupenums.RoleStandard,
}
case
// Additional permissions for moderators and administrators
CanUpdateStories,
CanDeleteStories:
return &RolePermission{
Permission: p,
Role: groupenums.RoleModerator,
}
case
// Additional permissions for administrators only
CanCreateUsers,
CanReadUsers,
CanUpdateUsers,
CanDeleteUsers,
CanCreateGroups,
CanReadGroups,
CanUpdateGroups,
CanDeleteGroups:
return &RolePermission{
Permission: p,
Role: groupenums.RoleAdmin,
}
}
// Illegal path - all permissions should have been handled above
panic(errors.New("Illegal path - all permissions should have been handled above"))
}
35 changes: 35 additions & 0 deletions internal/auth/permissions/validation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package permissions

import (
"net/http"
)

type PermissionGroup interface {
IsAuthorized(*http.Request) bool
}

type AllOf struct {
Groups []PermissionGroup
}

func (a AllOf) IsAuthorized(r *http.Request) bool {
for _, group := range a.Groups {
if !group.IsAuthorized(r) {
return false
}
}
return true
}

type AnyOf struct {
Groups []PermissionGroup
}

func (a AnyOf) IsAuthorized(r *http.Request) bool {
for _, group := range a.Groups {
if group.IsAuthorized(r) {
return true
}
}
return false
}
47 changes: 47 additions & 0 deletions internal/enums/groups/role.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package groupenums

type Role uint

const (
RoleStandard Role = iota
RoleModerator
RoleAdmin
)

// We cannot name it String() because it will conflict with the String() method
func (role Role) ToString() string {
switch role {
case RoleStandard:
return "user"
case RoleModerator:
return "moderator"
case RoleAdmin:
return "admin"
}
return "unknown"
}

func RoleFromString(role string) (Role, bool) {
switch role {
case "user":
return RoleStandard, true
case "moderator":
return RoleModerator, true
case "admin":
return RoleAdmin, true
}
// We fall back to standard role as default
return RoleStandard, false
}

func IsRoleGreaterThan(role1, role2 Role) bool {
switch role1 {
case RoleAdmin:
return true
case RoleModerator:
return role2 != RoleAdmin
case RoleStandard:
return role2 == RoleStandard
}
return false
}
Loading