wai-middleware-auth 0.2.3.1 → 0.2.4.1
raw patch · 5 files changed
+202/−8 lines, 5 filesdep +optparse-applicativePVP ok
version bump matches the API change (PVP)
Dependencies added: optparse-applicative
API changes (from Hackage documentation)
+ Network.Wai.Middleware.Auth.OAuth2.Gitlab: Gitlab :: Text -> Text -> [ByteString] -> OAuth2 -> Gitlab
+ Network.Wai.Middleware.Auth.OAuth2.Gitlab: [gitlabAPIUserEndpoint] :: Gitlab -> Text
+ Network.Wai.Middleware.Auth.OAuth2.Gitlab: [gitlabAppName] :: Gitlab -> Text
+ Network.Wai.Middleware.Auth.OAuth2.Gitlab: [gitlabEmailWhitelist] :: Gitlab -> [ByteString]
+ Network.Wai.Middleware.Auth.OAuth2.Gitlab: [gitlabOAuth2] :: Gitlab -> OAuth2
+ Network.Wai.Middleware.Auth.OAuth2.Gitlab: data Gitlab
+ Network.Wai.Middleware.Auth.OAuth2.Gitlab: gitlabParser :: ProviderParser
+ Network.Wai.Middleware.Auth.OAuth2.Gitlab: instance Data.Aeson.Types.FromJSON.FromJSON Network.Wai.Middleware.Auth.OAuth2.Gitlab.Gitlab
+ Network.Wai.Middleware.Auth.OAuth2.Gitlab: instance Data.Aeson.Types.FromJSON.FromJSON Network.Wai.Middleware.Auth.OAuth2.Gitlab.GitlabEmail
+ Network.Wai.Middleware.Auth.OAuth2.Gitlab: instance GHC.Show.Show Network.Wai.Middleware.Auth.OAuth2.Gitlab.GitlabEmail
+ Network.Wai.Middleware.Auth.OAuth2.Gitlab: instance Network.Wai.Middleware.Auth.Provider.AuthProvider Network.Wai.Middleware.Auth.OAuth2.Gitlab.Gitlab
+ Network.Wai.Middleware.Auth.OAuth2.Gitlab: mkGitlabProvider :: Text -> Text -> Text -> Text -> [ByteString] -> Maybe ProviderInfo -> Gitlab
Files
- CHANGELOG.md +4/−0
- README.md +42/−4
- app/Main.hs +13/−3
- src/Network/Wai/Middleware/Auth/OAuth2/Gitlab.hs +140/−0
- wai-middleware-auth.cabal +3/−1
CHANGELOG.md view
@@ -1,3 +1,7 @@+# 0.2.4.0++- Add GitLab provider.+ # 0.2.3.1 - Expose `discoverURI` in `Network.Wai.Middleware.Auth.OIDC`
README.md view
@@ -22,8 +22,8 @@ Google and Github. Configuration is done using a yaml config file. Here is a sample file that will-configure `wai-auth` to run a file server with google and github authentication-on `http://localhost:3000`:+configure `wai-auth` to run a file server with Google, GitHub, and GitLab+authentication on `http://localhost:3000`: ```yaml app_root: "_env:APPROOT:http://localhost:3000"@@ -46,6 +46,12 @@ client_secret: "...oxW" email_white_list: - "^[a-zA-Z0-9._%+-]+@example.com$"+ gitlab:+ client_id: "...9cfc"+ client_secret: "...f0d0"+ app_name: "Dev App for wai-middleware-auth"+ email_white_list:+ - "^[a-zA-Z0-9._%+-]+@example.com$" ``` Above configuration will also block access to users that don't have an email@@ -57,8 +63,9 @@ azuCFq0zEBkLSXhQrhliZzZD8Kblo... ``` -Make sure you have proper callback/redirect urls registered with google/github-apps, eg: `http://localhost:3000/_auth_middleware/google/complete`.+Make sure you have proper callback/redirect urls registered with+google/github/gitlab apps, eg:+`http://localhost:3000/_auth_middleware/google/complete`. After configuration file is ready, running application is very easy: @@ -67,3 +74,34 @@ Listening on port 3000 ``` +### Reverse proxy++To use a reverse proxy instead of a file server, replace `file_server` with+`reverse_proxy`, eg:++```yaml+reverse_proxy:+ host: myapp.example.com+ port: 80+```++### Self-hosted GitLab++The GitLab provider also supports using a self-hosted GitLab instance by+setting the `gitlab_host` field. In this case you may also want to override+the `provider_info` to change the title, logo, and description. For example:++```yaml+providers:+ gitlab:+ gitlab_host: gitlab.mycompany.com+ client_id: "...9cfc"+ client_secret: "...f0d0"+ app_name: "Dev App for wai-middleware-auth"+ email_white_list:+ - "^[a-zA-Z0-9._%+-]+@mycompany.com$"+ provider_info:+ title: My Company's GitLab+ logo_url: https://mycompany.com/logo.png+ descr: Use your My Company GitLab account to access this page.+```
app/Main.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TemplateHaskell #-}@@ -8,6 +9,7 @@ import Network.Wai.Handler.Warp (run) import Network.Wai.Middleware.Auth import Network.Wai.Middleware.Auth.OAuth2+import Network.Wai.Middleware.Auth.OAuth2.Gitlab import Network.Wai.Middleware.Auth.OAuth2.Github import Network.Wai.Middleware.Auth.OAuth2.Google import Network.Wai.Middleware.RequestLogger (logStdout)@@ -18,6 +20,14 @@ = ConfigFile FilePath | KeyFile KeyOptions +showHelpText :: ParseError+showHelpText = ShowHelpText+#if MIN_VERSION_optparse_applicative(0,16,0)+ Nothing+#endif+++ basicSettingsParser :: String -> Parser BasicOptions basicSettingsParser version = (ConfigFile <$>@@ -28,7 +38,7 @@ (InfoMsg version) (long "version" <> short 'v' <> help "Current version.") <* abortOption- ShowHelpText+ showHelpText (long "help" <> short 'h' <> help "Display this message.")) <|> (subparser (command@@ -60,7 +70,7 @@ (long "base64" <> short 'b' <> help "Produce a key in a base64 encoded form.") <* abortOption- ShowHelpText+ showHelpText (long "help" <> short 'h' <> help "Display this message.") @@ -76,7 +86,7 @@ case opts of ConfigFile configFile -> do authConfig <- readAuthConfig configFile- mkMain authConfig [githubParser, googleParser, oAuth2Parser] $ \port app -> do+ mkMain authConfig [gitlabParser, githubParser, googleParser, oAuth2Parser] $ \port app -> do putStrLn $ "Listening on port " ++ show port run port $ logStdout app KeyFile (KeyOptions {..}) -> do
+ src/Network/Wai/Middleware/Auth/OAuth2/Gitlab.hs view
@@ -0,0 +1,140 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+module Network.Wai.Middleware.Auth.OAuth2.Gitlab+ ( Gitlab(..)+ , mkGitlabProvider+ , gitlabParser+ ) where+import Control.Exception.Safe (catchAny)+import Data.Maybe (fromMaybe)+import Data.Aeson+import qualified Data.ByteString as S+import Data.Proxy (Proxy (..))+import qualified Data.Text as T+import Data.Text.Encoding (encodeUtf8)+import Network.HTTP.Simple (getResponseBody,+ httpJSON, parseRequest,+ setRequestHeaders)+import Network.HTTP.Types+import qualified Network.OAuth.OAuth2 as OA2+import Network.Wai.Auth.Internal (decodeToken)+import Network.Wai.Auth.Tools (getValidEmail)+import Network.Wai.Middleware.Auth.OAuth2+import Network.Wai.Middleware.Auth.Provider++-- | Create a gitlab authentication provider+--+-- @since 0.2.4.0+mkGitlabProvider+ :: T.Text -- ^ Hostname of GitLab instance (e.g. @gitlab.com@)+ -> T.Text -- ^ Name of the application as it is registered on gitlab+ -> T.Text -- ^ @client_id@ from gitlab+ -> T.Text -- ^ @client_secret@ from gitlab+ -> [S.ByteString] -- ^ White list of posix regular expressions for emails+ -- attached to gitlab account.+ -> Maybe ProviderInfo -- ^ Replacement for default info+ -> Gitlab+mkGitlabProvider gitlabHost appName clientId clientSecret emailWhiteList mProviderInfo =+ Gitlab+ appName+ ("https://" <> gitlabHost <> "/api/v4/user")+ emailWhiteList+ OAuth2+ { oa2ClientId = clientId+ , oa2ClientSecret = clientSecret+ , oa2AuthorizeEndpoint = ("https://" <> gitlabHost <> "/oauth/authorize")+ , oa2AccessTokenEndpoint = ("https://" <> gitlabHost <> "/oauth/token")+ , oa2Scope = Just ["read_user"]+ , oa2ProviderInfo = fromMaybe defProviderInfo mProviderInfo+ }+ where+ defProviderInfo =+ ProviderInfo+ { providerTitle = "GitLab"+ , providerLogoUrl =+ "https://about.gitlab.com/images/press/logo/png/gitlab-icon-rgb.png"+ , providerDescr = "Use your GitLab account to access this page."+ }++-- | Aeson parser for `Gitlab` provider.+--+-- @since 0.2.4.0+gitlabParser :: ProviderParser+gitlabParser = mkProviderParser (Proxy :: Proxy Gitlab)+++-- | Gitlab authentication provider+data Gitlab = Gitlab+ { gitlabAppName :: T.Text+ , gitlabAPIUserEndpoint :: T.Text+ , gitlabEmailWhitelist :: [S.ByteString]+ , gitlabOAuth2 :: OAuth2+ }++instance FromJSON Gitlab where+ parseJSON =+ withObject "Gitlab Provider Object" $ \obj -> do+ gitlabHost <- obj .:? "gitlab_host"+ appName <- obj .: "app_name"+ clientId <- obj .: "client_id"+ clientSecret <- obj .: "client_secret"+ emailWhiteList <- obj .:? "email_white_list" .!= []+ mProviderInfo <- obj .:? "provider_info"+ return $+ mkGitlabProvider+ (fromMaybe "gitlab.com" gitlabHost)+ appName+ clientId+ clientSecret+ (map encodeUtf8 emailWhiteList)+ mProviderInfo++-- | Newtype wrapper for a gitlab user+newtype GitlabEmail = GitlabEmail { gitlabEmail :: S.ByteString } deriving Show++instance FromJSON GitlabEmail where+ parseJSON = withObject "Gitlab Email" $ \ obj -> do+ email <- obj .: "email"+ return (GitlabEmail $ encodeUtf8 email)+++-- | Makes an API call to gitlab and retrieves user's verified email.+-- Note: we only retrieve the PRIMARY email, because there is no way+-- to tell whether secondary emails are verified or not.+retrieveUser :: T.Text -> T.Text -> S.ByteString -> IO GitlabEmail+retrieveUser appName userApiEndpoint accessToken = do+ req <- parseRequest (T.unpack userApiEndpoint)+ resp <- httpJSON $ setRequestHeaders headers req+ return $ getResponseBody resp+ where+ headers =+ [ ("Authorization", "Bearer " <> accessToken)+ , ("User-Agent", encodeUtf8 appName)+ ]+++instance AuthProvider Gitlab where+ getProviderName _ = "gitlab"+ getProviderInfo = getProviderInfo . gitlabOAuth2+ handleLogin Gitlab {..} req suffix renderUrl onSuccess onFailure = do+ let onOAuth2Success oauth2Tokens = do+ catchAny+ (do accessToken <-+ case decodeToken oauth2Tokens of+ Left err -> fail err+ Right tokens -> pure $ encodeUtf8 $ OA2.atoken $ OA2.accessToken tokens+ email <-+ gitlabEmail <$>+ retrieveUser+ gitlabAppName+ gitlabAPIUserEndpoint+ accessToken+ let mValidEmail = getValidEmail gitlabEmailWhitelist [email]+ case mValidEmail of+ Just validEmail -> onSuccess validEmail+ Nothing ->+ onFailure status403 $+ "Your primary email address does not have permission to access this resource. " <>+ "Please contact the administrator.")+ (\_err -> onFailure status501 "Issue communicating with gitlab")+ handleLogin gitlabOAuth2 req suffix renderUrl onOAuth2Success onFailure
wai-middleware-auth.cabal view
@@ -1,6 +1,6 @@ cabal-version: 1.18 name: wai-middleware-auth-version: 0.2.3.1+version: 0.2.4.1 synopsis: Authentication middleware that secures WAI application description: Please see the README and Haddocks at <https://www.stackage.org/package/wai-middleware-auth> license: MIT@@ -14,6 +14,7 @@ library exposed-modules: Network.Wai.Middleware.Auth Network.Wai.Middleware.Auth.OAuth2+ Network.Wai.Middleware.Auth.OAuth2.Gitlab Network.Wai.Middleware.Auth.OAuth2.Github Network.Wai.Middleware.Auth.OAuth2.Google Network.Wai.Middleware.Auth.OIDC@@ -72,6 +73,7 @@ , cereal , clientsession , optparse-simple+ , optparse-applicative , wai-extra , wai-middleware-auth , warp