202 lines
5.9 KiB
Go
202 lines
5.9 KiB
Go
package serverconfig
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
|
|
"bitbucket.org/nemt/nemt-portal-api/application/viewmodel"
|
|
"bitbucket.org/nemt/nemt-portal-api/infra/auth"
|
|
"bitbucket.org/nemt/nemt-portal-api/infra/config"
|
|
"bitbucket.org/nemt/nemt-portal-api/infra/logger"
|
|
"bitbucket.org/nemt/nemt-portal-api/server/router/routeutils"
|
|
"github.com/casbin/casbin"
|
|
"github.com/labstack/echo"
|
|
"github.com/labstack/echo/middleware"
|
|
|
|
"strings"
|
|
)
|
|
|
|
type (
|
|
// Config defines the config for CasbinAuth middleware.
|
|
Config struct {
|
|
// Skipper defines a function to skip middleware.
|
|
Skipper middleware.Skipper
|
|
|
|
// Enforcer CasbinAuth main rule.
|
|
// Required.
|
|
Enforcer *casbin.Enforcer
|
|
|
|
Application *config.Config
|
|
|
|
Svc *applicationservice.Service
|
|
Logger *logger.Logger
|
|
}
|
|
)
|
|
|
|
var (
|
|
// DefaultConfig is the default CasbinAuth middleware config.
|
|
DefaultConfig = Config{
|
|
Skipper: middleware.DefaultSkipper,
|
|
}
|
|
)
|
|
|
|
// MiddlewareWithConfig returns a CasbinAuth middleware with config.
|
|
// See `Middleware()`.
|
|
func MiddlewareWithConfig(cfg *config.Config, svc *applicationservice.Service, log *logger.Logger) echo.MiddlewareFunc {
|
|
|
|
config := &DefaultConfig
|
|
|
|
config.Enforcer = casbin.NewEnforcer("authorization_model.conf", "authorization_policy.csv")
|
|
|
|
config.Svc = svc
|
|
config.Logger = log
|
|
config.Application = cfg
|
|
|
|
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
|
return func(c echo.Context) error {
|
|
if config.Skipper(c) || config.CheckPermission(c) {
|
|
return next(c)
|
|
}
|
|
|
|
return routeutils.HandleHTMLError(c, echo.ErrForbidden)
|
|
}
|
|
}
|
|
}
|
|
|
|
func setAuthorizationMiddleware(e *echo.Echo, log *logger.Logger, cfg *config.Config, svc *applicationservice.Service) {
|
|
e.Use(MiddlewareWithConfig(cfg, svc, log))
|
|
}
|
|
|
|
// CheckPermission checks the user/method/path combination from the request.
|
|
// Returns true (permission granted) or false (permission forbidden)
|
|
func (a *Config) CheckPermission(c echo.Context) bool {
|
|
user, err := auth.GetUserDetail(c, a.Application)
|
|
if err != nil {
|
|
a.Logger.Warningf("Cannot get user details. %v\n", err)
|
|
user = viewmodel.User{}
|
|
}
|
|
method := c.Request().Method
|
|
path := c.Request().URL.Path
|
|
|
|
objectsRole, objectsOrganization, objectsOrganizationType, object := a.policyObjectAttributes(c, user)
|
|
|
|
currentUsersOrganization := viewmodel.Organization{}
|
|
if len(user.Organizations) > 0 {
|
|
currentUsersOrganization = user.Organizations[0]
|
|
}
|
|
|
|
currentUsersRole := viewmodel.Profile{}
|
|
if len(user.Profiles) > 0 {
|
|
currentUsersRole = user.Profiles[0]
|
|
}
|
|
|
|
currentUsersOrganizationType := ""
|
|
if len(user.Profiles) > 0 {
|
|
currentUsersOrganizationType = user.Profiles[0].Organization.Type.Key
|
|
}
|
|
|
|
orgRelation := organizationsRelation(currentUsersOrganization, objectsOrganization)
|
|
objRelation := a.objectRelation(object, user)
|
|
|
|
// parameters to Enforce must match the request section of the authorization_model.conf
|
|
return a.Enforcer.Enforce(currentUsersRole.Key,
|
|
objectsRole.Key,
|
|
objectsOrganizationType,
|
|
currentUsersOrganizationType,
|
|
orgRelation,
|
|
objRelation,
|
|
path,
|
|
method)
|
|
|
|
}
|
|
|
|
// policyObjectAttributes returns all information about the object being accessed for the policy
|
|
// in case object exists and returns users information if it is a new object
|
|
func (a *Config) policyObjectAttributes(c echo.Context, userDetails viewmodel.User) (viewmodel.Profile, viewmodel.Organization, string, interface{}) {
|
|
|
|
var object interface{}
|
|
|
|
const userIDParamName = "user_uuid"
|
|
existingUser := strings.Contains(c.Request().URL.Path, "/users/") && c.Param(userIDParamName) != ""
|
|
newUser := strings.Contains(c.Request().URL.Path, "/users/") && c.Param(userIDParamName) == ""
|
|
|
|
const organizationIDParamName = "org_uuid"
|
|
existingOrganization := strings.Contains(c.Request().URL.Path, "/organization") && c.Param(organizationIDParamName) != ""
|
|
newOrganization := strings.Contains(c.Request().URL.Path, "/organization") && c.Param(organizationIDParamName) == ""
|
|
|
|
fmt.Println("**********")
|
|
fmt.Printf("url %v\n", c.Param(userIDParamName))
|
|
fmt.Printf("user %v\n", userDetails.ID)
|
|
fmt.Printf("existing %v\n", existingUser)
|
|
fmt.Printf("new %v\n", newUser)
|
|
fmt.Println("**********")
|
|
|
|
switch {
|
|
case existingUser:
|
|
object, _ = a.Svc.Users.GetByUUID(c.Param(userIDParamName), "")
|
|
case newUser && len(userDetails.Organizations) > 0:
|
|
object = userDetails
|
|
case existingOrganization:
|
|
object, _ = a.Svc.Organization.GetByUUID(c.Param(organizationIDParamName), userDetails)
|
|
case newOrganization:
|
|
object = viewmodel.Organization{}
|
|
}
|
|
|
|
objectsRole := viewmodel.Profile{}
|
|
switch obj := object.(type) {
|
|
case viewmodel.User:
|
|
if len(obj.Profiles) > 0 {
|
|
objectsRole = obj.Profiles[0]
|
|
}
|
|
}
|
|
|
|
objectsOrganization := viewmodel.Organization{}
|
|
switch obj := object.(type) {
|
|
case viewmodel.User:
|
|
if len(obj.Profiles) > 0 {
|
|
objectsOrganization = obj.Profiles[0].Organization
|
|
}
|
|
case viewmodel.Organization:
|
|
objectsOrganization = obj
|
|
}
|
|
|
|
objectsOrganizationType := objectsOrganization.Type.Key
|
|
|
|
return objectsRole, objectsOrganization, objectsOrganizationType, object
|
|
}
|
|
|
|
func organizationsRelation(requestOrganization, currentUsersOrganization viewmodel.Organization) string {
|
|
if requestOrganization.UUID == currentUsersOrganization.UUID {
|
|
return "[equal]"
|
|
}
|
|
|
|
for _, childOrg := range currentUsersOrganization.ChildOrgs {
|
|
if childOrg.UUID == requestOrganization.UUID {
|
|
return "[equal-or-child]"
|
|
}
|
|
}
|
|
|
|
for _, childOrg := range requestOrganization.ChildOrgs {
|
|
if childOrg.UUID == currentUsersOrganization.UUID {
|
|
return "[parent]"
|
|
}
|
|
}
|
|
|
|
return "[unrelated]"
|
|
}
|
|
|
|
// organizationGoverningObject returns the role that is the owner of the object that is being accessed
|
|
// in case object exists and returns users role if it is a new object
|
|
func (a *Config) objectRelation(object interface{}, currentUser viewmodel.User) string {
|
|
|
|
switch obj := object.(type) {
|
|
case viewmodel.User:
|
|
if obj.ID == currentUser.ID {
|
|
return "[self]"
|
|
} else {
|
|
return "[other]"
|
|
}
|
|
}
|
|
return "[other]"
|
|
}
|