Files
old-svijetlastrana/server/serverconfig/authorization.go
2018-04-26 10:13:46 +02:00

172 lines
5.3 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
objectOrganization := a.organizationGoverningObject(c, user)
objectRole := a.roleGoverningObject(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]
}
orgRelation := organizationsRelation(currentUsersOrganization, objectOrganization)
objRelation := a.objectRelation(c, user)
// parameters to Enforce must match the request section of the authorization model
return a.Enforcer.Enforce(currentUsersRole.Key, objectRole.Key, orgRelation, objRelation, path, method)
}
// organizationGoverningObject returns the organization 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) organizationGoverningObject(c echo.Context, userDetails viewmodel.User) (result viewmodel.Organization) {
fmt.Println("***************")
fmt.Println(c.ParamValues())
fmt.Println("***************")
existingUser := strings.Contains(c.Request().URL.Path, "/users") && len(c.ParamValues()) > 1
newUser := strings.Contains(c.Request().URL.Path, "/users") && len(c.ParamValues()) <= 1
switch {
case existingUser:
user, _ := a.Svc.Users.GetByUUID(c.ParamValues()[1], "")
result = user.Organizations[0]
case newUser && len(userDetails.Organizations) > 0:
result = userDetails.Organizations[0]
}
return
}
// 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) roleGoverningObject(c echo.Context, userDetails viewmodel.User) (result viewmodel.Profile) {
existingUser := strings.Contains(c.Request().URL.Path, "/users") && len(c.ParamValues()) > 1
newUser := strings.Contains(c.Request().URL.Path, "/users") && len(c.ParamValues()) <= 1
switch {
case existingUser:
user, _ := a.Svc.Users.GetByUUID(c.ParamValues()[1], "")
result = user.Profiles[0]
case newUser && len(userDetails.Organizations) > 0:
result = userDetails.Profiles[0]
}
return
}
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(c echo.Context, userDetails viewmodel.User) string {
existingUser := strings.Contains(c.Request().URL.Path, "/users") && len(c.ParamValues()) > 1
switch {
case existingUser:
user, _ := a.Svc.Users.GetByUUID(c.ParamValues()[1], "")
if user.ID == userDetails.ID {
return "[self]"
}
}
return "[other]"
}