{-# LANGUAGE AllowAmbiguousTypes #-}

{-|
Module: IHP.Job.Dashboard.Auth
Description:  Authentication for Job dashboard
-}
module IHP.Job.Dashboard.Auth (
    AuthenticationMethod(..),
    NoAuth(..),
    BasicAuth(..),
    BasicAuthStatic(..),
) where

import IHP.Prelude
import GHC.TypeLits
import IHP.ControllerPrelude
import System.Environment (lookupEnv)

-- | Defines one method, 'authenticate', called before every action. Use to authenticate user.
--
-- Three implementations are provided:
-- - 'NoAuth' : No authentication
-- - 'BasicAuth' : HTTP Basic Auth using environment variables
-- - 'BasicAuthStatic' : HTTP Basic Auth using static values
--
-- Define your own implementation to use custom authentication for production.
class AuthenticationMethod a where
    authenticate :: (?context :: ControllerContext, ?modelContext :: ModelContext) => IO ()

-- | Don't use any authentication for jobs.
data NoAuth

-- | Authenticate using HTTP Basic Authentication by looking up username/password values
-- in environment variables given as type-level strings.
data BasicAuth (userEnv :: Symbol) (passEnv :: Symbol)

-- | Authenticate using HTTP Basic Authentication using username/password given as type level strings.
-- Meant for development only!
data BasicAuthStatic (user :: Symbol) (pass :: Symbol)

instance AuthenticationMethod NoAuth where
    authenticate :: IO ()
authenticate = () -> IO ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()

instance (KnownSymbol userEnv, KnownSymbol passEnv) => AuthenticationMethod (BasicAuth userEnv passEnv) where
    authenticate :: IO ()
authenticate = do
        (Maybe String, Maybe String)
creds <- (,) (Maybe String -> Maybe String -> (Maybe String, Maybe String))
-> IO (Maybe String)
-> IO (Maybe String -> (Maybe String, Maybe String))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> IO (Maybe String)
lookupEnv (Proxy userEnv -> String
forall (n :: Symbol) (proxy :: Symbol -> *).
KnownSymbol n =>
proxy n -> String
symbolVal (Proxy userEnv -> String) -> Proxy userEnv -> String
forall a b. (a -> b) -> a -> b
$ Proxy userEnv
forall k (t :: k). Proxy t
Proxy @userEnv) IO (Maybe String -> (Maybe String, Maybe String))
-> IO (Maybe String) -> IO (Maybe String, Maybe String)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> String -> IO (Maybe String)
lookupEnv (Proxy passEnv -> String
forall (n :: Symbol) (proxy :: Symbol -> *).
KnownSymbol n =>
proxy n -> String
symbolVal (Proxy passEnv -> String) -> Proxy passEnv -> String
forall a b. (a -> b) -> a -> b
$ Proxy passEnv
forall k (t :: k). Proxy t
Proxy @passEnv)
        case (Maybe String, Maybe String)
creds of
            (Just String
user, Just String
pass) -> (?context::ControllerContext) => Text -> Text -> Text -> IO ()
Text -> Text -> Text -> IO ()
basicAuth (String -> Text
forall a b. ConvertibleStrings a b => a -> b
cs String
user) (String -> Text
forall a b. ConvertibleStrings a b => a -> b
cs String
pass) Text
"jobs"
            (Maybe String, Maybe String)
_ -> Text -> IO ()
forall a. Text -> a
error Text
"Did not find HTTP Basic Auth credentials for Jobs Dashboard."

instance (KnownSymbol user, KnownSymbol pass) => AuthenticationMethod (BasicAuthStatic user pass) where
    authenticate :: IO ()
authenticate = (?context::ControllerContext) => Text -> Text -> Text -> IO ()
Text -> Text -> Text -> IO ()
basicAuth (String -> Text
forall a b. ConvertibleStrings a b => a -> b
cs (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ Proxy user -> String
forall (n :: Symbol) (proxy :: Symbol -> *).
KnownSymbol n =>
proxy n -> String
symbolVal (Proxy user -> String) -> Proxy user -> String
forall a b. (a -> b) -> a -> b
$ Proxy user
forall k (t :: k). Proxy t
Proxy @user) (String -> Text
forall a b. ConvertibleStrings a b => a -> b
cs (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ Proxy pass -> String
forall (n :: Symbol) (proxy :: Symbol -> *).
KnownSymbol n =>
proxy n -> String
symbolVal (Proxy pass -> String) -> Proxy pass -> String
forall a b. (a -> b) -> a -> b
$ Proxy pass
forall k (t :: k). Proxy t
Proxy @pass) Text
"jobs"