packages feed

project-m36-1.2.0: src/lib/ProjectM36/AccessControl.hs

-- | Applies access control lists to the current role to determine whether should be granted
{-# LANGUAGE RankNTypes #-}
module ProjectM36.AccessControl where
import ProjectM36.Base
import ProjectM36.Error
import ProjectM36.AccessControlList
import ProjectM36.TransactionGraph
import ProjectM36.RelationalExpression
import ProjectM36.DatabaseContext.Types
import ProjectM36.RelationVariablesMentioned
import ProjectM36.IsomorphicSchema
import ProjectM36.Function
import Control.Monad.Except
import Control.Monad (when, forM_)

applyACLRelationalExpr :: Show a => [RoleId] -> RelVarAccessControlList -> RelationalExprBase a -> Either RelationalError ()
applyACLRelationalExpr roleIds acl' expr = do
  let checkPerm perm =
        if hasAccess roleIds perm acl' then
          pure ()
          else
          Left (AccessDeniedError (SomeRelVarPermission perm))
  when (mentionsRelVar expr) (checkPerm AccessRelVarsPermission)

applyACLAlterTransGraphExpr :: [RoleId] -> AlterTransGraphAccessControlList -> AlterTransactionGraphExpr -> Either RelationalError ()
applyACLAlterTransGraphExpr roleIds acl' _alterExpr =
  if hasAccess roleIds CommitTransactionPermission acl' then
    pure ()
    else
    Left (AccessDeniedError (SomeAlterTransGraphPermission CommitTransactionPermission))

-- we should probably have finer-grained permission here
applyACLDatabaseContextIOExpr :: [RoleId] -> DatabaseContextIOExpr -> DatabaseContextIOEvalMonad ()
applyACLDatabaseContextIOExpr roleIds _expr = do
   acl' <- resolveIODBC acl  
   if hasAccess roleIds AlterFunctionPermission (dbcFunctionsACL acl') then
     pure ()
     else
     throwError (AccessDeniedError (SomeFunctionPermission AlterFunctionPermission))

applyACLDatabaseContextExpr :: [RoleId] -> DatabaseContextExpr' -> DatabaseContextEvalMonad ()
applyACLDatabaseContextExpr roleIds expr = do
  dbcAcl <- resolveDBC acl
  dbcFuncs <- resolveDBC dbcFunctions
  let checkRVPerm perm acl' =
        if hasAccess roleIds perm acl' then
          pure ()
        else
          dbErr (AccessDeniedError (SomeRelVarPermission perm))
{-      checkFuncPerm perm acl' =
        if hasAccess roleIds perm acl' then
          pure ()
        else
          dbErr (AccessDeniedError (SomeFunctionPermission perm))-}
      checkACLPerm perm acl' =
          if hasAccess roleIds perm acl' then
            pure ()
          else
            dbErr (AccessDeniedError (SomeACLPermission perm))
      checkFuncPerm perm acl' = do 
        -- check dbc-function-level permissions
        if hasAccess roleIds perm acl' then
          pure ()
          else
          dbErr (AccessDeniedError (SomeFunctionPermission perm))
      checkDBCFuncPerm fname perm = do
        -- check specific function-level permissions
          case functionForName fname dbcFuncs of
            Left err -> dbErr err
            Right func ->
              if hasAccess roleIds perm (funcACL func) then 
                pure ()
              else
                dbErr (AccessDeniedError (SomeDBCFunctionPermission perm))
      rvAcl = relvarsACL dbcAcl
      aclAcl = aclACL dbcAcl
      funcAcl = dbcFunctionsACL dbcAcl
  case expr of
    NoOperation -> pure ()
    Define{} ->
      checkRVPerm AccessRelVarsPermission rvAcl
    Undefine{} ->
      checkRVPerm AccessRelVarsPermission rvAcl
    Assign{} ->
      checkRVPerm AccessRelVarsPermission rvAcl
    Insert{} ->
      checkRVPerm AccessRelVarsPermission rvAcl
    Delete{} ->
      checkRVPerm AccessRelVarsPermission rvAcl
    Update{} ->
      checkRVPerm AccessRelVarsPermission rvAcl
    AddInclusionDependency{} ->
      checkRVPerm AccessRelVarsPermission rvAcl
    RemoveInclusionDependency{} ->
      checkRVPerm AccessRelVarsPermission rvAcl
    AddNotification{} ->
      checkRVPerm AccessRelVarsPermission rvAcl
    RemoveNotification{} ->
      checkRVPerm AccessRelVarsPermission rvAcl
    AddTypeConstructor{} ->
      checkRVPerm AccessRelVarsPermission rvAcl
    RemoveTypeConstructor{} ->
      checkRVPerm AccessRelVarsPermission rvAcl
    RemoveAtomFunction{} ->
      checkRVPerm AccessRelVarsPermission rvAcl
    RemoveDatabaseContextFunction fname -> do
      checkFuncPerm AlterFunctionPermission funcAcl
      checkDBCFuncPerm fname AlterDBCFunctionPermission 
    ExecuteDatabaseContextFunction fname _args -> do
      checkFuncPerm ExecuteFunctionPermission funcAcl
      checkDBCFuncPerm fname ExecuteDBCFunctionPermission       
    AddRegisteredQuery{} ->
      checkRVPerm AccessRelVarsPermission rvAcl
    RemoveRegisteredQuery{} ->
      checkRVPerm AccessRelVarsPermission rvAcl
    AlterACL{} ->
      checkACLPerm AlterACLPermission aclAcl
    MultipleExpr exprs ->
      mapM_ (applyACLDatabaseContextExpr roleIds) exprs
{-
applyACLTransGraphRelationalExpr :: [RoleId] -> RelVarAccessControlList -> TransGraphRelationalExpr -> Either RelationalError ()
applyACLTransGraphRelationalExpr roleIds acl' expr =
  when (mentionsRelVar expr) $
    if hasAccess roleIds AccessRelVarsPermission acl' then
      pure ()
      else
      Left (AccessDeniedError (SomeRelVarPermission AccessRelVarsPermission))
-}

-- | Validate that any reference to a relvar within a transaction has AccessRelVarsPermission at that transactionId. Pass relevant access control lists extracted from the original expression which reference relvars at specific transaction ids.
applyACLGraphRefRelationalExpr :: [RoleId] -> [RelVarAccessControlList] -> Either RelationalError ()
applyACLGraphRefRelationalExpr roleIds transACLs =
  forM_ transACLs $ \acl' -> applyACLRelationalExpr roleIds acl' (RelationVariable "true" ())
    
applyACLSchemaExpr :: [RoleId] -> SchemaAccessControlList -> SchemaExpr -> Either RelationalError ()
applyACLSchemaExpr roleIds acl' _expr =
  if hasAccess roleIds AlterSchemaPermission acl' then
    pure ()
    else
    Left (AccessDeniedError (SomeAlterSchemaPermission AlterSchemaPermission))