Use Permissions type in the API
This commit is contained in:
parent
c0c71b1203
commit
b1d02f68a0
|
@ -114,7 +114,7 @@ authBearer Nothing m = m
|
||||||
authBearer (Just (BearerToken bearer)) m = do
|
authBearer (Just (BearerToken bearer)) m = do
|
||||||
let getUserPermissions = do
|
let getUserPermissions = do
|
||||||
Right DBToken {..} <- lift $ dbGetTokenBySecret bearer
|
Right DBToken {..} <- lift $ dbGetTokenBySecret bearer
|
||||||
permissions' <- fromMaybe mempty . readPermission <$> lift dbTokenPermissions
|
permissions' <- lift dbTokenPermissions
|
||||||
DBUser {..} <- lift dbTokenUser
|
DBUser {..} <- lift dbTokenUser
|
||||||
userID <- lift dbUserId
|
userID <- lift dbUserId
|
||||||
pure (Just userID, permissions')
|
pure (Just userID, permissions')
|
||||||
|
@ -140,8 +140,7 @@ authBasic :: Maybe BasicAuth -> ActionT LText APIM a -> ActionT LText APIM a
|
||||||
authBasic Nothing m = m
|
authBasic Nothing m = m
|
||||||
authBasic (Just basic) m = do
|
authBasic (Just basic) m = do
|
||||||
DBUser {..} <- verifyBasic basic
|
DBUser {..} <- verifyBasic basic
|
||||||
permissions <- readPermission <$> lift dbUserPermissions >>=
|
permissions <- lift dbUserPermissions
|
||||||
fromMaybeFail status500 "Internal server error"
|
|
||||||
userID <- lift dbUserId
|
userID <- lift dbUserId
|
||||||
flip local m $ \state -> state
|
flip local m $ \state -> state
|
||||||
{ stateCurrentUser = Just userID
|
{ stateCurrentUser = Just userID
|
||||||
|
@ -166,7 +165,7 @@ newtype APIM a = APIM (ReaderT RequestState IO a)
|
||||||
|
|
||||||
data RequestState = RequestState
|
data RequestState = RequestState
|
||||||
{ stateCurrentUser :: Maybe UserID
|
{ stateCurrentUser :: Maybe UserID
|
||||||
, statePermissions :: Map Scope Permission
|
, statePermissions :: Permissions
|
||||||
, stateConfig :: Config
|
, stateConfig :: Config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,28 +213,13 @@ instance MonadRandom APIM where
|
||||||
getRandomBytes = liftIO . getRandomBytes
|
getRandomBytes = liftIO . getRandomBytes
|
||||||
|
|
||||||
instance MonadPermissions APIM where
|
instance MonadPermissions APIM where
|
||||||
currentPermissions = show <$> asks statePermissions
|
currentPermissions = asks statePermissions
|
||||||
defaultPermissions = pure $ show $ (fromList [(OwnProfile, ReadWrite)] :: Map Scope Permission)
|
defaultPermissions = pure $ Permissions $ fromList [(OwnProfile, ReadWrite)]
|
||||||
toPermissions = pure . fmap show . readPermission
|
|
||||||
hasPermission scope permission = (>= permission) <$> findPermission scope
|
|
||||||
where findPermission :: Scope -> APIM Permission
|
|
||||||
findPermission scope@(Profile user) = selfPermissions scope user OwnProfile
|
|
||||||
findPermission scope@(Tokens user) = selfPermissions scope user OwnTokens
|
|
||||||
findPermission scope = findPermission' scope <$> asks statePermissions
|
|
||||||
findPermission' :: Scope -> Map Scope Permission -> Permission
|
|
||||||
findPermission' = findWithDefault None
|
|
||||||
selfPermissions :: Scope -> UserID -> Scope -> APIM Permission
|
|
||||||
selfPermissions scope user own = do
|
|
||||||
isSelf <- (Just user ==) <$> currentUser
|
|
||||||
let f = if isSelf then max <$> findPermission' own <*> findPermission' scope
|
|
||||||
else findPermission' scope
|
|
||||||
|
|
||||||
f <$> asks statePermissions
|
|
||||||
|
|
||||||
runAPIM :: Config -> APIM a -> IO a
|
runAPIM :: Config -> APIM a -> IO a
|
||||||
runAPIM config (APIM m) = runReaderT m RequestState
|
runAPIM config (APIM m) = runReaderT m RequestState
|
||||||
{ stateCurrentUser = Nothing
|
{ stateCurrentUser = Nothing
|
||||||
, statePermissions = fromList []
|
, statePermissions = Permissions $ fromList []
|
||||||
, stateConfig = config
|
, stateConfig = config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ import "cryptonite" Crypto.Random (getRandomBytes, MonadRandom)
|
||||||
import qualified "base64" Data.ByteString.Base64 as B64
|
import qualified "base64" Data.ByteString.Base64 as B64
|
||||||
|
|
||||||
import Control.Monad.Except (MonadError, throwError, catchError)
|
import Control.Monad.Except (MonadError, throwError, catchError)
|
||||||
|
import qualified Data.Map as Map
|
||||||
import Data.Morpheus.Server (deriveApp, runApp)
|
import Data.Morpheus.Server (deriveApp, runApp)
|
||||||
import Data.Morpheus.Server.Types (defaultRootResolver, RootResolver(..), Undefined)
|
import Data.Morpheus.Server.Types (defaultRootResolver, RootResolver(..), Undefined)
|
||||||
import Data.Morpheus.Types (Arg(..), GQLType, GQLError, App)
|
import Data.Morpheus.Types (Arg(..), GQLType, GQLError, App)
|
||||||
|
@ -108,7 +109,7 @@ newTokenArgsToData :: (MonadRandom m, MonadTime m, MonadPermissions m) =>
|
||||||
newTokenArgsToData NewTokenArgs {..} user = do
|
newTokenArgsToData NewTokenArgs {..} user = do
|
||||||
tokenData <- B64.encodeBase64 <$> getRandomBytes 128
|
tokenData <- B64.encodeBase64 <$> getRandomBytes 128
|
||||||
issued <- currentTime
|
issued <- currentTime
|
||||||
permissions <- maybe currentPermissions pure =<< maybe (pure Nothing) toPermissions permissions
|
permissions <- maybe currentPermissions pure $ inputPermissionsToPermissions <$> permissions
|
||||||
let expires = Nothing
|
let expires = Nothing
|
||||||
pure NewTokenData {..}
|
pure NewTokenData {..}
|
||||||
|
|
||||||
|
@ -155,7 +156,7 @@ resolveQuery = Query
|
||||||
, user = \(Arg id) -> targetUser id >>= \user -> requirePermission (Profile user) ReadOnly >>
|
, user = \(Arg id) -> targetUser id >>= \user -> requirePermission (Profile user) ReadOnly >>
|
||||||
(Just . dbUserToUser <$> (dbGetUser user >>= liftDBEither)) `catchError` const (pure Nothing)
|
(Just . dbUserToUser <$> (dbGetUser user >>= liftDBEither)) `catchError` const (pure Nothing)
|
||||||
, applications = requirePermission Applications ReadOnly >> map dbUserToUser <$> dbGetApplications
|
, applications = requirePermission Applications ReadOnly >> map dbUserToUser <$> dbGetApplications
|
||||||
, permissions = currentPermissions
|
, permissions = fromPermissions <$> currentPermissions
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveMutation :: (MonadRequest m, MonadEmail m, MonadRandom m, MonadTime m,
|
resolveMutation :: (MonadRequest m, MonadEmail m, MonadRandom m, MonadTime m,
|
||||||
|
@ -209,7 +210,7 @@ data NewKeyArgs = NewKeyArgs { comment :: Maybe Text, keyData :: Base64, expires
|
||||||
deriving (Generic, GQLType, Eq, Show)
|
deriving (Generic, GQLType, Eq, Show)
|
||||||
|
|
||||||
data NewTokenArgs = NewTokenArgs
|
data NewTokenArgs = NewTokenArgs
|
||||||
{ comment :: Maybe Text, name :: Maybe Text, permissions :: Maybe Text }
|
{ comment :: Maybe Text, name :: Maybe Text, permissions :: Maybe [InputScopePermission] }
|
||||||
deriving (Generic, GQLType)
|
deriving (Generic, GQLType)
|
||||||
|
|
||||||
data User m = User
|
data User m = User
|
||||||
|
@ -223,7 +224,7 @@ data User m = User
|
||||||
, homeplace :: m Text
|
, homeplace :: m Text
|
||||||
, registered :: m Time
|
, registered :: m Time
|
||||||
, accepted :: m (Maybe Time)
|
, accepted :: m (Maybe Time)
|
||||||
, permissions :: m Text
|
, permissions :: m [ScopePermission]
|
||||||
, isMember :: m Bool
|
, isMember :: m Bool
|
||||||
, application :: m Text
|
, application :: m Text
|
||||||
, tokens :: m [Token m]
|
, tokens :: m [Token m]
|
||||||
|
@ -246,14 +247,14 @@ data Token m = Token
|
||||||
, comment :: m Text
|
, comment :: m Text
|
||||||
, issued :: m Time
|
, issued :: m Time
|
||||||
, expires :: m (Maybe Time)
|
, expires :: m (Maybe Time)
|
||||||
, permissions :: m Text
|
, permissions :: m [ScopePermission]
|
||||||
} deriving (Generic, GQLType)
|
} deriving (Generic, GQLType)
|
||||||
|
|
||||||
data Query m = Query
|
data Query m = Query
|
||||||
{ users :: m [User m]
|
{ users :: m [User m]
|
||||||
, user :: Arg "id" (Maybe UserID) -> m (Maybe (User m))
|
, user :: Arg "id" (Maybe UserID) -> m (Maybe (User m))
|
||||||
, applications :: m [User m]
|
, applications :: m [User m]
|
||||||
, permissions :: m Text
|
, permissions :: m [ScopePermission]
|
||||||
} deriving (Generic, GQLType)
|
} deriving (Generic, GQLType)
|
||||||
|
|
||||||
data Mutation m = Mutation
|
data Mutation m = Mutation
|
||||||
|
@ -279,7 +280,7 @@ dbUserToUser DBUser {..} = User
|
||||||
, homeplace = dbUserHomeplace
|
, homeplace = dbUserHomeplace
|
||||||
, registered = dbUserRegistered
|
, registered = dbUserRegistered
|
||||||
, accepted = dbUserAccepted
|
, accepted = dbUserAccepted
|
||||||
, permissions = dbUserPermissions
|
, permissions = fromPermissions <$> dbUserPermissions
|
||||||
, isMember = isJust <$> dbUserAccepted
|
, isMember = isJust <$> dbUserAccepted
|
||||||
, application = dbUserApplication
|
, application = dbUserApplication
|
||||||
, tokens = dbUserId >>= flip requirePermission ReadOnly . Tokens >> map dbTokenToToken <$> dbUserTokens
|
, tokens = dbUserId >>= flip requirePermission ReadOnly . Tokens >> map dbTokenToToken <$> dbUserTokens
|
||||||
|
@ -304,5 +305,19 @@ dbTokenToToken DBToken {..} = Token
|
||||||
, comment = dbTokenComment
|
, comment = dbTokenComment
|
||||||
, issued = dbTokenIssued
|
, issued = dbTokenIssued
|
||||||
, expires = dbTokenExpires
|
, expires = dbTokenExpires
|
||||||
, permissions = dbTokenPermissions
|
, permissions = fromPermissions <$> dbTokenPermissions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inputPermissionsToPermissions :: [InputScopePermission] -> Permissions
|
||||||
|
inputPermissionsToPermissions = Permissions .
|
||||||
|
Map.fromList . map (\InputScopePermission {..} -> (scope, permission))
|
||||||
|
|
||||||
|
fromPermissions :: Permissions -> [ScopePermission]
|
||||||
|
fromPermissions (Permissions perms) =
|
||||||
|
map (\(scope, permission) -> ScopePermission scope permission) $ Map.toList perms
|
||||||
|
|
||||||
|
data InputScopePermission = InputScopePermission {scope :: Scope, permission :: Permission}
|
||||||
|
deriving (Generic, GQLType, Eq, Show)
|
||||||
|
|
||||||
|
data ScopePermission = ScopePermission {scope :: Scope, permission :: Permission}
|
||||||
|
deriving (Generic, GQLType)
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
module Datarekisteri.Backend.Sql.Queries where
|
module Datarekisteri.Backend.Sql.Queries where
|
||||||
|
|
||||||
|
import Control.Monad (void)
|
||||||
import Control.Monad.Logger (LoggingT)
|
import Control.Monad.Logger (LoggingT)
|
||||||
import Data.Aeson (fromJSON, toJSON, Result(..))
|
import Data.Aeson (fromJSON, toJSON, Result(..))
|
||||||
import Data.Maybe (listToMaybe)
|
import Data.Maybe (listToMaybe)
|
||||||
|
@ -46,11 +47,11 @@ getKeys user = select $ do
|
||||||
getKey :: KeyID -> SqlM (Maybe SqlKey)
|
getKey :: KeyID -> SqlM (Maybe SqlKey)
|
||||||
getKey = get . fromID
|
getKey = get . fromID
|
||||||
|
|
||||||
getPermissions :: UserID -> SqlM (Maybe Text)
|
getPermissions :: UserID -> SqlM (Maybe Permissions)
|
||||||
getPermissions user = fmap (fmap sqlUserPermissions) $ get $ fromID user
|
getPermissions user = fmap (fmap sqlUserPermissions) $ get $ fromID user
|
||||||
|
|
||||||
setPermissions :: UserID -> Text -> SqlM ()
|
setPermissions :: UserID -> Permissions -> SqlM ()
|
||||||
setPermissions user txt = updateUserData user [SqlUserPermissions Persist.=. txt] [] >> return ()
|
setPermissions user permissions = void $ updateUserData user [SqlUserPermissions Persist.=. permissions] []
|
||||||
|
|
||||||
getPrimaryKey :: UserID -> SqlM (Maybe (Entity SqlKey))
|
getPrimaryKey :: UserID -> SqlM (Maybe (Entity SqlKey))
|
||||||
getPrimaryKey user = fmap listToMaybe $ select $ do
|
getPrimaryKey user = fmap listToMaybe $ select $ do
|
||||||
|
|
|
@ -28,7 +28,7 @@ mkPersist sqlSettings [persistUpperCase|
|
||||||
SqlUser sql=users
|
SqlUser sql=users
|
||||||
registered Time
|
registered Time
|
||||||
passwordCrypt PasswordHash
|
passwordCrypt PasswordHash
|
||||||
permissions Text
|
permissions Permissions
|
||||||
accepted (Maybe Time)
|
accepted (Maybe Time)
|
||||||
memberData Value sqltype=jsonb
|
memberData Value sqltype=jsonb
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ SqlToken sql=tokens
|
||||||
comment Text
|
comment Text
|
||||||
issued Time
|
issued Time
|
||||||
expires (Maybe Time)
|
expires (Maybe Time)
|
||||||
permissions Text
|
permissions Permissions
|
||||||
|
|
||||||
UniqueNameUid name uid
|
UniqueNameUid name uid
|
||||||
UniqueData data
|
UniqueData data
|
||||||
|
|
|
@ -36,7 +36,7 @@ forward :: Monad m => [a] -> m [Maybe a]
|
||||||
forward = pure . map Just
|
forward = pure . map Just
|
||||||
|
|
||||||
requirePermission :: (MonadPermissions m, MonadError GQLError m) => Scope -> Permission -> m ()
|
requirePermission :: (MonadPermissions m, MonadError GQLError m) => Scope -> Permission -> m ()
|
||||||
requirePermission scope permission = unlessM (hasPermission scope permission) $
|
requirePermission scope permission = unlessM (hasPermission scope permission <$> currentPermissions) $
|
||||||
throwError $ "Insufficient permissions, " <> show permission <> " for "
|
throwError $ "Insufficient permissions, " <> show permission <> " for "
|
||||||
<> show scope <> " required."
|
<> show scope <> " required."
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ data ApplicationData = ApplicationData
|
||||||
, registered :: Time
|
, registered :: Time
|
||||||
, verificationSecret :: Text
|
, verificationSecret :: Text
|
||||||
, passwordHash :: PasswordHash
|
, passwordHash :: PasswordHash
|
||||||
, permissions :: Text
|
, permissions :: Permissions
|
||||||
} deriving (Generic, Eq, Show)
|
} deriving (Generic, Eq, Show)
|
||||||
|
|
||||||
data UpdateData = UpdateData
|
data UpdateData = UpdateData
|
||||||
|
@ -99,7 +99,7 @@ data Page a m = Page { pageData :: m a, cursor :: m (Maybe Cursor) }
|
||||||
data NewTokenData = NewTokenData
|
data NewTokenData = NewTokenData
|
||||||
{ comment :: Maybe Text
|
{ comment :: Maybe Text
|
||||||
, name :: Maybe Text
|
, name :: Maybe Text
|
||||||
, permissions :: Text
|
, permissions :: Permissions
|
||||||
, tokenData :: Text
|
, tokenData :: Text
|
||||||
, issued :: Time
|
, issued :: Time
|
||||||
, expires :: Maybe Time
|
, expires :: Maybe Time
|
||||||
|
@ -122,7 +122,7 @@ data DBUser m = DBUser
|
||||||
, dbUserHomeplace :: m Text
|
, dbUserHomeplace :: m Text
|
||||||
, dbUserRegistered :: m Time
|
, dbUserRegistered :: m Time
|
||||||
, dbUserAccepted :: m (Maybe Time)
|
, dbUserAccepted :: m (Maybe Time)
|
||||||
, dbUserPermissions :: m Text
|
, dbUserPermissions :: m Permissions
|
||||||
, dbUserApplication :: m Text
|
, dbUserApplication :: m Text
|
||||||
, dbUserPasswordHash :: m PasswordHash
|
, dbUserPasswordHash :: m PasswordHash
|
||||||
, dbUserTokens :: m [DBToken m]
|
, dbUserTokens :: m [DBToken m]
|
||||||
|
@ -146,7 +146,7 @@ data DBToken m = DBToken
|
||||||
, dbTokenComment :: m Text
|
, dbTokenComment :: m Text
|
||||||
, dbTokenIssued :: m Time
|
, dbTokenIssued :: m Time
|
||||||
, dbTokenExpires :: m (Maybe Time)
|
, dbTokenExpires :: m (Maybe Time)
|
||||||
, dbTokenPermissions :: m Text
|
, dbTokenPermissions :: m Permissions
|
||||||
}
|
}
|
||||||
|
|
||||||
class Monad m => MonadTime m where
|
class Monad m => MonadTime m where
|
||||||
|
@ -180,10 +180,8 @@ class Monad m => MonadRequest m where
|
||||||
currentUser :: m (Maybe UserID)
|
currentUser :: m (Maybe UserID)
|
||||||
|
|
||||||
class Monad m => MonadPermissions m where
|
class Monad m => MonadPermissions m where
|
||||||
hasPermission :: Scope -> Permission -> m Bool
|
currentPermissions :: m Permissions
|
||||||
currentPermissions :: m Text
|
defaultPermissions :: m Permissions
|
||||||
defaultPermissions :: m Text
|
|
||||||
toPermissions :: Text -> m (Maybe Text)
|
|
||||||
|
|
||||||
instance (MonadDB m, LiftOperation o) => MonadDB (Resolver o () m) where
|
instance (MonadDB m, LiftOperation o) => MonadDB (Resolver o () m) where
|
||||||
dbUpdateUser = fmap (fmap liftUser) . lift . dbUpdateUser
|
dbUpdateUser = fmap (fmap liftUser) . lift . dbUpdateUser
|
||||||
|
@ -204,10 +202,8 @@ instance (MonadRequest m, LiftOperation o) => MonadRequest (Resolver o () m) whe
|
||||||
currentUser = lift currentUser
|
currentUser = lift currentUser
|
||||||
|
|
||||||
instance (MonadPermissions m, LiftOperation o) => MonadPermissions (Resolver o () m) where
|
instance (MonadPermissions m, LiftOperation o) => MonadPermissions (Resolver o () m) where
|
||||||
hasPermission scope permission = lift $ hasPermission scope permission
|
|
||||||
defaultPermissions = lift defaultPermissions
|
defaultPermissions = lift defaultPermissions
|
||||||
currentPermissions = lift currentPermissions
|
currentPermissions = lift currentPermissions
|
||||||
toPermissions = lift . toPermissions
|
|
||||||
|
|
||||||
instance (MonadEmail m, LiftOperation o) => MonadEmail (Resolver o () m) where
|
instance (MonadEmail m, LiftOperation o) => MonadEmail (Resolver o () m) where
|
||||||
sendEmail = lift . sendEmail
|
sendEmail = lift . sendEmail
|
||||||
|
|
|
@ -43,7 +43,7 @@ addUserMain AddUserOpts {..} = do
|
||||||
userID <- Sql.addUser $ SqlUser
|
userID <- Sql.addUser $ SqlUser
|
||||||
{ sqlUserRegistered = time
|
{ sqlUserRegistered = time
|
||||||
, sqlUserPasswordCrypt = passwordHash
|
, sqlUserPasswordCrypt = passwordHash
|
||||||
, sqlUserPermissions = show addUserPermissions
|
, sqlUserPermissions = addUserPermissions
|
||||||
, sqlUserAccepted = Just time
|
, sqlUserAccepted = Just time
|
||||||
, sqlUserMemberData = toJSON $ MemberData
|
, sqlUserMemberData = toJSON $ MemberData
|
||||||
{ nickname = addUserNickname
|
{ nickname = addUserNickname
|
||||||
|
@ -82,7 +82,7 @@ updateUserMain UpdateUserOpts {..} = runQuery $ do
|
||||||
Just application -> Sql.updateUserData updateUserId [] [Sql.SetUserApplication application]
|
Just application -> Sql.updateUserData updateUserId [] [Sql.SetUserApplication application]
|
||||||
case updateUserPermissions of
|
case updateUserPermissions of
|
||||||
Nothing -> pure ()
|
Nothing -> pure ()
|
||||||
Just permissions -> Sql.setPermissions updateUserId $ show permissions
|
Just permissions -> Sql.setPermissions updateUserId permissions
|
||||||
|
|
||||||
cliOptions :: Parser CLIOptions
|
cliOptions :: Parser CLIOptions
|
||||||
cliOptions = CLIOptions
|
cliOptions = CLIOptions
|
||||||
|
@ -114,8 +114,8 @@ updateUserCommand = fmap UpdateUser $ UpdateUserOpts
|
||||||
<*> optional (strOption (long "application" <> metavar "TEXT"))
|
<*> optional (strOption (long "application" <> metavar "TEXT"))
|
||||||
<*> optional permissionsParser
|
<*> optional permissionsParser
|
||||||
|
|
||||||
permissionsParser :: Parser (Map Scope Permission)
|
permissionsParser :: Parser Permissions
|
||||||
permissionsParser = fromList <$> many permissionParser
|
permissionsParser = Permissions . fromList <$> many permissionParser
|
||||||
|
|
||||||
permissionParser :: Parser (Scope, Permission)
|
permissionParser :: Parser (Scope, Permission)
|
||||||
permissionParser = (,)
|
permissionParser = (,)
|
||||||
|
@ -136,7 +136,7 @@ data CLISubCommand = AddUser AddUserOpts
|
||||||
data UpdateUserOpts = UpdateUserOpts
|
data UpdateUserOpts = UpdateUserOpts
|
||||||
{ updateUserId :: UserID
|
{ updateUserId :: UserID
|
||||||
, updateUserApplication :: Maybe Text
|
, updateUserApplication :: Maybe Text
|
||||||
, updateUserPermissions :: Maybe (Map Scope Permission)
|
, updateUserPermissions :: Maybe Permissions
|
||||||
}
|
}
|
||||||
|
|
||||||
data AddUserOpts = AddUserOpts
|
data AddUserOpts = AddUserOpts
|
||||||
|
@ -146,7 +146,7 @@ data AddUserOpts = AddUserOpts
|
||||||
, addUserHomeplace :: Text
|
, addUserHomeplace :: Text
|
||||||
, addUserPhoneNumber :: PhoneNumber
|
, addUserPhoneNumber :: PhoneNumber
|
||||||
, addUserEmail :: Email
|
, addUserEmail :: Email
|
||||||
, addUserPermissions :: Map Scope Permission
|
, addUserPermissions :: Permissions
|
||||||
, addUserApplication :: Text
|
, addUserApplication :: Text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ module Datarekisteri.Core.Types
|
||||||
, addTime
|
, addTime
|
||||||
, base64Decode
|
, base64Decode
|
||||||
, base64Encode
|
, base64Encode
|
||||||
, readPermission
|
, hasPermission
|
||||||
, renderDate
|
, renderDate
|
||||||
, renderEmail
|
, renderEmail
|
||||||
, renderPhoneNumber
|
, renderPhoneNumber
|
||||||
|
@ -40,6 +40,7 @@ import qualified "base64" Data.ByteString.Base64 as B64
|
||||||
import Data.Aeson (Value, ToJSON(..), FromJSON(..), Result(..), ToJSONKey(..), FromJSONKey(..), FromJSONKeyFunction(..), ToJSONKeyFunction(..), fromJSON, eitherDecodeStrict)
|
import Data.Aeson (Value, ToJSON(..), FromJSON(..), Result(..), ToJSONKey(..), FromJSONKey(..), FromJSONKeyFunction(..), ToJSONKeyFunction(..), fromJSON, eitherDecodeStrict)
|
||||||
import Data.Aeson.Encoding (string)
|
import Data.Aeson.Encoding (string)
|
||||||
import Data.Char (isSpace)
|
import Data.Char (isSpace)
|
||||||
|
import qualified Data.Map as Map
|
||||||
import Data.Morpheus.Server.Types (SCALAR, TYPE)
|
import Data.Morpheus.Server.Types (SCALAR, TYPE)
|
||||||
import Data.Morpheus.Types (GQLType, DecodeScalar(..), KIND, EncodeScalar(..), ScalarValue(..))
|
import Data.Morpheus.Types (GQLType, DecodeScalar(..), KIND, EncodeScalar(..), ScalarValue(..))
|
||||||
import Data.Morpheus.Types.GQLScalar (scalarToJSON, scalarFromJSON)
|
import Data.Morpheus.Types.GQLScalar (scalarToJSON, scalarFromJSON)
|
||||||
|
@ -120,6 +121,10 @@ instance GQLType Permission where type KIND Permission = SCALAR
|
||||||
instance ToJSON Permission where toJSON = scalarToJSON
|
instance ToJSON Permission where toJSON = scalarToJSON
|
||||||
instance FromJSON Permission where parseJSON = scalarFromJSON <=< parseJSON
|
instance FromJSON Permission where parseJSON = scalarFromJSON <=< parseJSON
|
||||||
|
|
||||||
|
hasPermission :: Scope -> Permission -> Permissions -> Bool
|
||||||
|
hasPermission scope permission (Permissions permissionMap) =
|
||||||
|
Map.findWithDefault None scope permissionMap >= permission
|
||||||
|
|
||||||
newtype Permissions = Permissions (Map Scope Permission)
|
newtype Permissions = Permissions (Map Scope Permission)
|
||||||
deriving (Show, Eq, Read, Generic, ToJSON, FromJSON, Semigroup, Monoid)
|
deriving (Show, Eq, Read, Generic, ToJSON, FromJSON, Semigroup, Monoid)
|
||||||
|
|
||||||
|
@ -137,9 +142,6 @@ instance Ord Permissions where
|
||||||
(Permissions a) <= (Permissions b) =
|
(Permissions a) <= (Permissions b) =
|
||||||
and $ Map.intersectionWith (<=) a $ b <> (const None <$> Map.difference a b)
|
and $ Map.intersectionWith (<=) a $ b <> (const None <$> Map.difference a b)
|
||||||
|
|
||||||
readPermission :: Text -> Maybe Permissions
|
|
||||||
readPermission = rightToMaybe . readEither . toString
|
|
||||||
|
|
||||||
newtype PhoneNumber = PhoneNumber Text deriving (Show, Generic)
|
newtype PhoneNumber = PhoneNumber Text deriving (Show, Generic)
|
||||||
|
|
||||||
renderPhoneNumber :: PhoneNumber -> Text
|
renderPhoneNumber :: PhoneNumber -> Text
|
||||||
|
|
|
@ -6,8 +6,12 @@ scalar Email
|
||||||
|
|
||||||
scalar KeyID
|
scalar KeyID
|
||||||
|
|
||||||
|
scalar Permission
|
||||||
|
|
||||||
scalar PhoneNumber
|
scalar PhoneNumber
|
||||||
|
|
||||||
|
scalar Scope
|
||||||
|
|
||||||
scalar Time
|
scalar Time
|
||||||
|
|
||||||
scalar TokenID
|
scalar TokenID
|
||||||
|
@ -19,6 +23,11 @@ enum Unit {
|
||||||
Unit2
|
Unit2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input InputScopePermission {
|
||||||
|
scope: Scope!
|
||||||
|
permission: Permission!
|
||||||
|
}
|
||||||
|
|
||||||
type PGPKey {
|
type PGPKey {
|
||||||
id: KeyID!
|
id: KeyID!
|
||||||
pgpKeyData: Base64!
|
pgpKeyData: Base64!
|
||||||
|
@ -27,6 +36,11 @@ type PGPKey {
|
||||||
comment: String!
|
comment: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ScopePermission {
|
||||||
|
scope: Scope!
|
||||||
|
permission: Permission!
|
||||||
|
}
|
||||||
|
|
||||||
type Token {
|
type Token {
|
||||||
id: TokenID!
|
id: TokenID!
|
||||||
name: String
|
name: String
|
||||||
|
@ -34,7 +48,7 @@ type Token {
|
||||||
comment: String!
|
comment: String!
|
||||||
issued: Time!
|
issued: Time!
|
||||||
expires: Time
|
expires: Time
|
||||||
permissions: String!
|
permissions: [ScopePermission!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
type User {
|
type User {
|
||||||
|
@ -48,7 +62,7 @@ type User {
|
||||||
homeplace: String!
|
homeplace: String!
|
||||||
registered: Time!
|
registered: Time!
|
||||||
accepted: Time
|
accepted: Time
|
||||||
permissions: String!
|
permissions: [ScopePermission!]!
|
||||||
isMember: Boolean!
|
isMember: Boolean!
|
||||||
application: String!
|
application: String!
|
||||||
tokens: [Token!]!
|
tokens: [Token!]!
|
||||||
|
@ -60,7 +74,7 @@ type Query {
|
||||||
users: [User!]!
|
users: [User!]!
|
||||||
user(id: UserID): User
|
user(id: UserID): User
|
||||||
applications: [User!]!
|
applications: [User!]!
|
||||||
permissions: String!
|
permissions: [ScopePermission!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
|
@ -68,7 +82,7 @@ type Mutation {
|
||||||
verifyEmail(secret: String!): Boolean!
|
verifyEmail(secret: String!): Boolean!
|
||||||
resendVerificationEmail(user: UserID): Unit!
|
resendVerificationEmail(user: UserID): Unit!
|
||||||
update(email: Email, phoneNumber: PhoneNumber, password: String, name: String, nickname: String, homeplace: String, application: String, user: UserID): User!
|
update(email: Email, phoneNumber: PhoneNumber, password: String, name: String, nickname: String, homeplace: String, application: String, user: UserID): User!
|
||||||
newToken(comment: String, name: String, permissions: String): Token!
|
newToken(comment: String, name: String, permissions: [InputScopePermission!]): Token!
|
||||||
newKey(comment: String, keyData: Base64!, expires: Time): PGPKey!
|
newKey(comment: String, keyData: Base64!, expires: Time): PGPKey!
|
||||||
accept(user: UserID!): Unit!
|
accept(user: UserID!): Unit!
|
||||||
reject(user: UserID!): Unit!
|
reject(user: UserID!): Unit!
|
||||||
|
|
|
@ -29,10 +29,12 @@ import Yesod
|
||||||
import Yesod.Auth
|
import Yesod.Auth
|
||||||
import Yesod.Static
|
import Yesod.Static
|
||||||
|
|
||||||
import Datarekisteri.Core.Types (UserID(..), Scope(..), Permission(..), readPermission)
|
import Datarekisteri.Core.Types (UserID(..), Scope(..), Permission(..), Permissions(..))
|
||||||
import Datarekisteri.Frontend.ApiRequests
|
import Datarekisteri.Frontend.ApiRequests
|
||||||
import Datarekisteri.Frontend.Auth
|
import Datarekisteri.Frontend.Auth
|
||||||
|
|
||||||
|
import qualified Datarekisteri.Core.Types as Core
|
||||||
|
|
||||||
data DataIdClient = DataIdClient
|
data DataIdClient = DataIdClient
|
||||||
{ getStatic :: Static
|
{ getStatic :: Static
|
||||||
, getConfig :: Config
|
, getConfig :: Config
|
||||||
|
@ -70,7 +72,10 @@ mkYesodData "DataIdClient" [parseRoutes|
|
||||||
|
|
||||||
declareLocalTypesInline "schema.gql" [raw|
|
declareLocalTypesInline "schema.gql" [raw|
|
||||||
query GetPermissions {
|
query GetPermissions {
|
||||||
permissions
|
permissions {
|
||||||
|
scope
|
||||||
|
permission
|
||||||
|
}
|
||||||
user {
|
user {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
@ -98,18 +103,20 @@ instance YesodAuth DataIdClient where
|
||||||
withAuthenticated :: (AuthId DataIdClient -> Handler AuthResult) -> Handler AuthResult
|
withAuthenticated :: (AuthId DataIdClient -> Handler AuthResult) -> Handler AuthResult
|
||||||
withAuthenticated m = maybeAuthId >>= maybe (pure AuthenticationRequired) m
|
withAuthenticated m = maybeAuthId >>= maybe (pure AuthenticationRequired) m
|
||||||
|
|
||||||
|
readPermissions :: [GetPermissionsPermissions] -> Permissions
|
||||||
|
readPermissions = Permissions . fromList . map (\GetPermissionsPermissions {..} -> (scope, permission))
|
||||||
|
|
||||||
hasPermission :: Scope -> Permission -> Handler Bool
|
hasPermission :: Scope -> Permission -> Handler Bool
|
||||||
hasPermission scope permission = do
|
hasPermission scope permission = do
|
||||||
GetPermissions {..} <- apiRequest @GetPermissions False ()
|
GetPermissions {..} <- apiRequest @GetPermissions False ()
|
||||||
let permissionMap = fromMaybe mempty $ readPermission permissions
|
let permissions' = readPermissions permissions
|
||||||
findPermission scope = findWithDefault None scope permissionMap
|
|
||||||
userID = (\x -> id (x :: GetPermissionsUser)) <$> user
|
userID = (\x -> id (x :: GetPermissionsUser)) <$> user
|
||||||
scopes = scope :| case scope of
|
scopes = scope :| case scope of
|
||||||
Tokens uid | Just uid == userID -> [OwnTokens]
|
Tokens uid | Just uid == userID -> [OwnTokens]
|
||||||
Profile uid | Just uid == userID -> [OwnProfile]
|
Profile uid | Just uid == userID -> [OwnProfile]
|
||||||
_ -> []
|
_ -> []
|
||||||
-- TODO add Members and Applications to Profile Scopes if profile owner is member/applicant
|
-- TODO add Members and Applications to Profile Scopes if profile owner is member/applicant
|
||||||
pure $ (>= permission) $ maximum1 $ findPermission <$> scopes
|
pure $ any (\scope -> Core.hasPermission scope permission permissions') scopes
|
||||||
|
|
||||||
requirePermission :: Scope -> Permission -> Handler AuthResult
|
requirePermission :: Scope -> Permission -> Handler AuthResult
|
||||||
requirePermission scope permission = ifM (hasPermission scope permission)
|
requirePermission scope permission = ifM (hasPermission scope permission)
|
||||||
|
|
Loading…
Reference in New Issue