packages feed

keycloak-hs 2.0.2 → 2.0.3

raw patch · 7 files changed

+138/−31 lines, 7 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

Files

README.md view
@@ -2,7 +2,7 @@ ===========  Keycloak-hs is an Haskell library for connecting to [Keycloak](https://www.keycloak.org/).-Keycloak allows to authenticate users and protect API resources.+Keycloak allows to authenticate and manage users and to protect API resources. This library allows you to retrieve and analyse Keycloak authentication tokens, and to protect resources in your API.  Install
examples/Main.hs view
@@ -18,8 +18,10 @@  main :: IO () main = do+  -- Keycloak-hs has logging, you can enable it for debugging.   updateGlobalLogger rootLoggerName (setLevel DEBUG) +  -- We run all the commands in the 'Keycloak' Monad.   void $ flip runKeycloak kcConfig $ do     liftIO $ putStrLn "Starting tests..."   @@ -71,9 +73,12 @@     liftIO $ putStrLn $ "User 'demo' can access resource 'demo': " ++ (show isAuth)        -- We can also retrieve all the permissions for our user.-    perms <- getPermissions [PermReq Nothing [ScopeName "view"]] userToken+    --perms <- getPermissions [PermReq Nothing [ScopeName "view"]] userToken   -    liftIO $ putStrLn $ "All permissions: " ++ (show perms)+    --liftIO $ putStrLn $ "All permissions: " ++ (show perms)        --resources can be deleted     --deleteResource resId clientToken++    users <- getUsers Nothing Nothing Nothing jwt+    liftIO $ putStrLn $ "All Users: " ++ (show users)
keycloak-hs.cabal view
@@ -1,6 +1,6 @@ cabal-version: 1.12 name: keycloak-hs-version: 2.0.2+version: 2.0.3 license: BSD3 license-file: LICENSE copyright: 2019 Corentin Dupont
src/Keycloak/Authorizations.hs view
@@ -1,6 +1,32 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} +{-|+This module helps you manage resources authorization with Keycloak.++In Keycloak, in the client, activate "Authorization Enabled" and set "Valid Redirect URIs" as "*".+You then need to create your scopes, policies and permissions in the authorization tab.+If you are unsure, set the "Policy Enforcement Mode" as permissive, so that a positive permission will be given with resources without policy.++The example below shows how to retrieve a token from Keycloak, and then retrieve the permissions of a user on a specific resource.++@+-- Let's get a token for a specific user login/password+userToken <- getJWT "demo" "demo"++-- Can I access this resource?+isAuth <- isAuthorized resId (ScopeName "view") userToken++liftIO $ putStrLn $ "User 'demo' can access resource 'demo': " ++ (show isAuth)++-- We can also retrieve all the permissions for our user.+perms <- getPermissions [PermReq Nothing [ScopeName "view"]] userToken++liftIO $ putStrLn $ "All permissions: " ++ (show perms)+@++-}+ module Keycloak.Authorizations where  import           Control.Monad.Reader as R
src/Keycloak/Tokens.hs view
@@ -3,6 +3,50 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ViewPatterns #-} +{-|+Authentication with Keycloak is based on [JWTs](https://jwt.io/).+This module helps you retrieve tokens from Keycloak, and use them to authenticate your users.+In Keycloak, you need to configure a realm, a client and a user.++Users can also have additional attributes.+To see them in the Token, you need to add "protocol mappers" in the Client, that will copy the User attribute in the Token.++The example below retrieves a User token using Login/password, verifies it, and extract all the user details from it.++@+-- Kecyloak configuration.+kcConfig :: KCConfig+kcConfig = KCConfig {+  _confBaseUrl       = "http://localhost:8080/auth",+  _confRealm         = "demo",+  _confClientId      = "demo",+  _confClientSecret  = "3d792576-4e56-4c58-991a-49074e6a92ea"}++main :: IO ()+main = do++  void $ flip runKeycloak kcConfig $ do+    liftIO $ putStrLn "Starting tests..."+  +    -- JWKs are public keys delivered by Keycloak to check the integrity of any JWT (user tokens).+    -- an application may retrieve these keys once at startup and keep them.+    jwks <- getJWKs+    liftIO $ putStrLn $ "Got JWKs: \n" ++ (show jwks) ++ "\n\n"+  +    -- Get a JWT from Keycloak. A JWT can then be used to authenticate yourself with an application.+    jwt <- getJWT "demo" "demo" +    liftIO $ putStrLn $ "Got JWT: \n" ++ (show jwt) ++ "\n\n"+  +    -- Retrieve the claims contained in the JWT.+    claims <- verifyJWT (head jwks) jwt+    liftIO $ putStrLn $ "Claims decoded from Token: \n" ++ (show claims) ++ "\n\n"+    +    -- get the user from the claim+    let user = getClaimsUser claims+    liftIO $ putStrLn $ "User decoded from claims: \n" ++ (show user) ++ "\n\n"+@+-}+ module Keycloak.Tokens where  import           Control.Lens hiding ((.=))@@ -35,7 +79,8 @@   return jwks  --- | Retrieve the user's token. This token can be used for every other Keycloak calls.+-- | Retrieve the user's token. This token can be used to authenticate the user.+-- This token can be also used for every other Keycloak calls. getJWT :: Username -> Password -> Keycloak JWT getJWT username password = do    debug "Get user token"
src/Keycloak/Types.hs view
@@ -21,11 +21,14 @@ import           Network.HTTP.Client as HC hiding (responseBody) import           Crypto.JWT as JWT +-- | Our Json Web Token as returned by Keycloak type JWT = SignedJWT  -- * Keycloak Monad  -- | Keycloak Monad stack: a simple Reader monad containing the config, and an ExceptT to handle HTTPErrors and parse errors.+-- You can extract the value using 'runKeycloak'.+-- Example: @keys <- runKeycloak getJWKs defaultKCConfig@ type Keycloak a = ReaderT KCConfig (ExceptT KCError IO) a  -- | Contains HTTP errors and parse errors.@@ -46,10 +49,11 @@  -- | Configuration of Keycloak. data KCConfig = KCConfig {-  _confBaseUrl       :: Text,-  _confRealm         :: Text,-  _confClientId      :: Text,-  _confClientSecret  :: Text} deriving (Eq, Show)+  _confBaseUrl       :: Text,  -- ^ Base url where Keycloak resides+  _confRealm         :: Text,  -- ^ realm to use+  _confClientId      :: Text,  -- ^ client id+  _confClientSecret  :: Text}  -- ^ client secret, found in Client/Credentials tab+  deriving (Eq, Show)  -- | Default configuration defaultKCConfig :: KCConfig@@ -92,7 +96,8 @@  -- * Permissions --- | Scope name+-- | Scope name, such as "houses:view"+-- You need to create the scopes in Client/Authorization panel/Authorization scopes tab newtype ScopeName = ScopeName {unScopeName :: Text} deriving (Eq, Generic, Ord, Hashable)  --JSON instances@@ -127,11 +132,26 @@ instance FromJSON Scope where   parseJSON = genericParseJSON defaultOptions {fieldLabelModifier = unCapitalize . drop 5} +-- | permission request+-- You can perform a request on a specific resourse, or on all resources.+-- You can request permission on multiple scopes at once.+-- +data PermReq = PermReq +  { permReqResourceId :: Maybe ResourceId, -- ^ Requested ressource Ids. Nothing means "All resources".+    permReqScopes     :: [ScopeName]       -- ^ Scopes requested. [] means "all scopes".+  } deriving (Generic, Eq, Ord, Hashable)++instance Show PermReq where+  show (PermReq (Just (ResourceId res1)) scopes) = (show res1) <> " " <> (show scopes)+  show (PermReq Nothing scopes)                  = "none " <> (show scopes)+ -- | Keycloak permission on a resource+-- Returned by Keycloak after a permission request is made.+--  data Permission = Permission -  { permRsid   :: Maybe ResourceId,   -- Resource ID, can be Nothing in case of scope-only permission request-    permRsname :: Maybe ResourceName, -- Resrouce Name-    permScopes :: [ScopeName]         -- Scopes that are accessible Non empty+  { permRsid   :: Maybe ResourceId,   -- ^ Resource ID, can be Nothing in case of scope-only permission request+    permRsname :: Maybe ResourceName, -- ^ Resource Name, can be Nothing in case of scope-only permission request+    permScopes :: [ScopeName]         -- ^ Scopes that are accessible (Non empty)   } deriving (Generic, Show, Eq)  instance ToJSON Permission where@@ -140,18 +160,8 @@ instance FromJSON Permission where   parseJSON = genericParseJSON defaultOptions {fieldLabelModifier = unCapitalize . drop 4} --- | permission request-data PermReq = PermReq -  { permReqResourceId :: Maybe ResourceId, -- Requested ressource Ids. Nothing means "All resources".-    permReqScopes     :: [ScopeName]       -- Scopes requested. [] means "all scopes".-  } deriving (Generic, Eq, Ord, Hashable) -instance Show PermReq where-  show (PermReq (Just (ResourceId res1)) scopes) = (show res1) <> " " <> (show scopes)-  show (PermReq Nothing scopes)                  = "none " <> (show scopes) -- -- * User  type Username = Text@@ -222,15 +232,17 @@   parseJSON = genericParseJSON (defaultOptions {unwrapUnaryRecords = True})  -- | A complete resource+-- Resources are created in Keycloak in Client/+-- You can create resources in Client/Authorization panel/Resources scopes tab data Resource = Resource {-     resId                 :: Maybe ResourceId,-     resName               :: ResourceName,-     resType               :: Maybe ResourceType,-     resUris               :: [Text],-     resScopes             :: [Scope],-     resOwner              :: Owner,-     resOwnerManagedAccess :: Bool,-     resAttributes         :: [Attribute]+     resId                 :: Maybe ResourceId,   -- ^ the Keycloak resource ID+     resName               :: ResourceName,       -- ^ the Keycloak resource name+     resType               :: Maybe ResourceType, -- ^ Optional resource type+     resUris               :: [Text],             -- ^ Optional resource URI+     resScopes             :: [Scope],            -- ^ All the possible scopes for that resource+     resOwner              :: Owner,              -- ^ The Owner or the resource+     resOwnerManagedAccess :: Bool,               -- ^ Whether the owner can manage his own resources (e.g. resource sharing with others)+     resAttributes         :: [Attribute]         -- ^ Resource attributes   } deriving (Generic, Show)  instance FromJSON Resource where
src/Keycloak/Users.hs view
@@ -3,6 +3,25 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ViewPatterns #-} +{-|+This module helps you manage users in Keycloak.+You can create, read and update users.+To activate this, you need to give the role "manage users" to your user in Keycloak.+For this, go in your user, select the "Role mappings" tab.+Then in "client Roles", select "realm management" and assign the role "manage-users".++Example usage:++@+-- Get a JWT from Keycloak. A JWT can then be used to authenticate yourself.+jwt <- getJWT "demo" "demo" ++users <- getUsers Nothing Nothing Nothing jwt+liftIO $ putStrLn $ "All Users: " ++ (show users)+@++-}+ module Keycloak.Users where  import           Control.Monad.Except (throwError)