-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AuthMethods + Template Haskell experiment #1918
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
{-# LANGUAGE DeriveDataTypeable #-} | ||
{-# LANGUAGE DuplicateRecordFields #-} | ||
{-# LANGUAGE TemplateHaskell #-} | ||
|
||
module Wasp.AppSpec.App.Auth.AuthMethods | ||
( generateAuthMethods, | ||
userSignupFieldsForEmailAuth, | ||
userSignupFieldsForUsernameAuth, | ||
userSignupFieldsForExternalAuth, | ||
isAuthMethodExternal, | ||
AuthMethod (..), | ||
UsernameAndPasswordConfig (..), | ||
ExternalAuthConfig (..), | ||
EmailAuthConfig (..), | ||
) | ||
where | ||
|
||
import Data.Data (Data) | ||
import Language.Haskell.TH | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unqalified, unlisted import -> not a good practice! |
||
import Language.Haskell.TH.Syntax (VarBangType) | ||
import Wasp.AppSpec.App.Auth.EmailVerification (EmailVerificationConfig) | ||
import Wasp.AppSpec.App.Auth.PasswordReset (PasswordResetConfig) | ||
import Wasp.AppSpec.App.EmailSender (EmailFromField) | ||
import Wasp.AppSpec.ExtImport (ExtImport) | ||
import Wasp.Util (toLowerFirst) | ||
|
||
data AuthMethod = UsernameAndPassword | Email | Google | Keycloak | GitHub deriving (Show, Enum, Bounded) | ||
|
||
data UsernameAndPasswordConfig = UsernameAndPasswordConfig | ||
{ userSignupFields :: Maybe ExtImport | ||
} | ||
deriving (Show, Eq, Data) | ||
|
||
data ExternalAuthConfig = ExternalAuthConfig | ||
{ configFn :: Maybe ExtImport, | ||
userSignupFields :: Maybe ExtImport | ||
} | ||
deriving (Show, Eq, Data) | ||
|
||
data EmailAuthConfig = EmailAuthConfig | ||
{ userSignupFields :: Maybe ExtImport, | ||
fromField :: EmailFromField, | ||
emailVerification :: EmailVerificationConfig, | ||
passwordReset :: PasswordResetConfig | ||
} | ||
deriving (Show, Eq, Data) | ||
|
||
configType :: AuthMethod -> Name | ||
configType UsernameAndPassword = ''UsernameAndPasswordConfig | ||
configType Email = ''EmailAuthConfig | ||
configType Google = ''ExternalAuthConfig | ||
configType Keycloak = ''ExternalAuthConfig | ||
configType GitHub = ''ExternalAuthConfig | ||
|
||
-- Generate the AuthMethods data type | ||
-- data AuthMethods = AuthMethods | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems to me like you ended up defining this relationship anyway above! Becauwe you defined it via |
||
-- { usernameAndPassword :: Maybe UsernameAndPasswordConfig, | ||
-- google :: Maybe ExternalAuthConfig, | ||
-- gitHub :: Maybe ExternalAuthConfig, | ||
-- keycloak :: Maybe ExternalAuthConfig, | ||
-- email :: Maybe EmailAuthConfig | ||
-- ... | ||
-- } deriving (Show, Eq, Data) | ||
generateAuthMethods :: Q [Dec] | ||
generateAuthMethods = do | ||
let authMethodsName = mkName "AuthMethods" | ||
let authMethodsCtorName = mkName "AuthMethods" | ||
let fields = generateField <$> [UsernameAndPassword .. GitHub] | ||
-- data AuthMethods | ||
let authMethods = | ||
dataD | ||
(cxt []) | ||
authMethodsName | ||
[] | ||
Nothing | ||
[recC authMethodsCtorName fields] | ||
[derivClause Nothing [[t|Show|], [t|Eq|], [t|Data|]]] | ||
sequence [authMethods] | ||
where | ||
-- usernameAndPassword :: Maybe UsernameAndPasswordConfig | ||
generateField :: AuthMethod -> Q VarBangType | ||
generateField authMethod = do | ||
let fieldName = mkName (toLowerFirst (show authMethod)) | ||
let fieldConfigType = configType authMethod | ||
let fieldType = appT (conT ''Maybe) (conT fieldConfigType) | ||
let fieldStrictness = bang noSourceUnpackedness noSourceStrictness | ||
varBangType fieldName $ bangType fieldStrictness fieldType | ||
|
||
-- These helper functions are used to avoid ambiguity when using the | ||
-- `userSignupFields` function (otherwise we need to use the DuplicateRecordFields | ||
-- extension in each module that uses them). | ||
userSignupFieldsForEmailAuth :: EmailAuthConfig -> Maybe ExtImport | ||
userSignupFieldsForEmailAuth = userSignupFields | ||
|
||
userSignupFieldsForUsernameAuth :: UsernameAndPasswordConfig -> Maybe ExtImport | ||
userSignupFieldsForUsernameAuth = userSignupFields | ||
|
||
userSignupFieldsForExternalAuth :: ExternalAuthConfig -> Maybe ExtImport | ||
userSignupFieldsForExternalAuth = userSignupFields | ||
Comment on lines
+92
to
+99
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What you can do, if you want is, use typeclass for something like this, which has a method |
||
|
||
isAuthMethodExternal :: AuthMethod -> Bool | ||
isAuthMethodExternal Google = True | ||
isAuthMethodExternal Keycloak = True | ||
isAuthMethodExternal GitHub = True | ||
isAuthMethodExternal _ = False |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
module Wasp.AppSpec.App.Auth.IsEnabled where | ||
|
||
import Language.Haskell.TH | ||
import Wasp.AppSpec.App.Auth (Auth (..), AuthMethods (..), generateIsAuthMethodEnabled) | ||
import Wasp.AppSpec.App.Auth.AuthMethods (AuthMethod(..), isAuthMethodExternal) | ||
|
||
$(generateIsAuthMethodEnabled) | ||
|
||
isExternalAuthEnabled :: Auth -> Bool | ||
isExternalAuthEnabled auth = any check [minBound .. maxBound :: AuthMethod] | ||
where | ||
check method = isAuthMethodExternal method && isAuthMethodEnabled method auth |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since TH can be hard to undrsatnd, and here it certainly is a bit ahrd to read, it would be good if you could document here, in a comment above it, what does this TH generate.