Skip to content

Commit

Permalink
Incremental updates
Browse files Browse the repository at this point in the history
  • Loading branch information
jonkri committed Mar 18, 2024
1 parent 4e8f34e commit afbb088
Show file tree
Hide file tree
Showing 36 changed files with 612 additions and 314 deletions.
5 changes: 4 additions & 1 deletion .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ variables:

build:
stage: build
image: registry.nejla.com/nejla-ab/docker-images/nejla-build:f9e4283c2ac701a26cc8a21a0782f5dbd51c446e
before_script:
- eval $(ssh-agent -s)
- ssh-add <(echo "$AUTHSERVICE_SSH_PRIVKEY")
Expand All @@ -23,10 +24,12 @@ build:
- scripts/registry-login
script:
- make -C service dist/auth-service tests
- make dist/openapi.json
artifacts:
paths:
- service/dist
- dist/doc
- dist/openapi.json
cache:
paths:
- '.stack-cache'
Expand Down Expand Up @@ -118,7 +121,7 @@ test-backend:
POSTGRES_HOST_AUTH_METHOD: trust
DB_HOST: database
services:
- name: postgres:10.13
- name: postgres:16
alias: database
- name: mailhog/mailhog:latest
alias: mailhog
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile.build
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Dockerfile that builds the service from source
# E.g. for publication on Dockerhub

FROM haskell:8.10.7 as build
FROM haskell:9.6.4 as build

RUN export DEBIAN_FRONTEND=noninteractive && \
apt-get update && \
Expand All @@ -16,7 +16,7 @@ WORKDIR /service
RUN mkdir /dist && \
stack --stack-yaml stack.dockerhub.yaml build --install-ghc --copy-bins --local-bin-path /dist -j2

FROM ubuntu:focal
FROM ubuntu:jammy

RUN export DEBIAN_FRONTEND=noninteractive && \
apt-get update && \
Expand Down
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ PROXY_IMAGE=$(REGISTRY)/$(PROXY_IMAGE_NAME)
COMPOSE=docker-compose -f devel/docker-compose.yaml --project-directory=$(PWD)

.PHONY: all
all: auth-service-proxy.image dist/doc
all: auth-service-proxy.image dist/doc dist/openapi.json
$(MAKE) -C service all

.PHONY: dist/doc
Expand All @@ -23,6 +23,12 @@ dist/doc:
# pandoc Doc/API.md -o dist/doc/index.html
cp -f Doc/API.html dist/doc/index.html

.PHONY: dist/openapi.json
dist/openapi.json:
$(MAKE) -C auth-service-core dist/openapi.json
mkdir -p dist
cp -f auth-service-core/dist/openapi.json dist/openapi.json

.PHONY: service/image
service/image:
$(MAKE) -C service image
Expand Down
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -618,8 +618,6 @@ auth-service supports single sign-on via SAML 2.0. **SP-initiated** and
**IdP-initiated** login flows are supported via **POST response bindings**
(again to `/api/sso/assert`) if `allow_unsolicited_responses` is set to `true`.

The IdP **must not require client signatures**.

Three attributes are supported, of which two are required. The **name** and
**email** attributes are **required**. The **role** attribute is **optional**.
Multiple role attributes may be specified; however, joined values are **not**
Expand Down
5 changes: 4 additions & 1 deletion SAML.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ that should have SAML enabled.
signed assertions. (the "realm certificate")
* `config`: a simple "key=value" encoded configuration for the instance, with
the following fields:
* `request-signing.key.pem` (optional): RSA private key file for signing authn
requests. If this file is missing, requests signing is disabled.

|Option|Required|Type|Default|Description|
|------|--------|----|-------|-----------|
Expand All @@ -76,6 +78,7 @@ that should have SAML enabled.
|`redirect_after_login`| No| String| `/`| URL to redirect after SAML login succeeds|
|`allow_unencrypted_assertions`| No| Boolean| false| Accept unencrypted assertions from the IdP (encrypted assertions are always accepted). Note that the encryption key still needs to be set.|
|`allow_unsolicited_responses`| No| Boolean| false| Accept unsolicited auth responses,e.g. IdP-initiated SSO.|
|`request_signing_digest`| No| String | sha256 | Digest to use when signing authentication requests. Has to be one of `sha1` or `sha256` but the use of `sha1` is discouraged.

Example for the `config` file:
```
Expand All @@ -91,4 +94,4 @@ redirect_after_login=/index.html
## Testing SAML SSO:

If the example server is running, you can navigate your browser to
`http://localhost:8000/api/sso/login` to test the SSO login
`http://localhost:8000/api/sso/login` to test the SSO login.
14 changes: 9 additions & 5 deletions auth-service-core/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,36 @@ build-env-file:=$(env-file)
endif
include $(build-env-file)

.PHONY: all
all: dist/openapi-gen dist/doc dist/openapi.json

srcfiles:=$(shell find src -type f)
test-srcfiles = $(shell find test-suite -type f)

dist/lib: $(srcfiles) $(test-srcfiles) auth-service-core.cabal stack.yaml
dist/openapi-gen: $(srcfiles) $(test-srcfiles) auth-service-core.cabal stack.yaml
rm -f stack.yaml.lock
mkdir -p ./dist
stack build --install-ghc --test --no-run-tests \
${stack_args} \
${stack_build_args} \
--haddock --no-haddock-deps --haddock-hyperlink-source
git rev-parse HEAD > dist/lib
--haddock --no-haddock-deps --haddock-hyperlink-source \
--copy-bins --local-bin-path ./dist


dist/doc: dist/lib
dist/doc: dist/openapi-gen
rm -rf dist/doc
cp -fr $(shell stack path ${stack_args} --dist-dir)/doc/html/auth-service-core \
dist/doc

dist/openapi.json: dist/openapi-gen
dist/openapi-gen dist/openapi.json

# Tests
#######

tests = dist/tests/tests

$(tests): dist/tests/% : dist/lib $(srcfiles) $(test-srcfiles) auth-service-core.cabal stack.yaml
$(tests): dist/tests/% : dist/openapi-gen $(srcfiles) $(test-srcfiles) auth-service-core.cabal stack.yaml
mkdir -p dist/tests
cp "$(shell stack ${stack_args} path --dist-dir)/build/$(notdir $@)/$(notdir $@)" dist/tests/

Expand Down
16 changes: 14 additions & 2 deletions auth-service-core/auth-service-core.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ library
exposed-modules: AuthService.Types
, AuthService.Api
, AuthService.SignedHeaders
, AuthService.OpenAPI
, SignedAuth
, SignedAuth.Headers
, SignedAuth.JWS
Expand All @@ -27,6 +28,7 @@ library
, SignedAuth.Util
other-modules: Compat
, Helpers
, AuthService.OpenAPI.Schema
other-extensions: DeriveDataTypeable
build-depends: base >=4.9
, aeson >=1.0
Expand All @@ -49,8 +51,8 @@ library
, path-pieces
, servant
, servant-server
, servant-swagger
, swagger2
, servant-openapi3
, openapi3
, text >=1.2
, time
, uuid
Expand All @@ -71,6 +73,15 @@ library
-Wno-name-shadowing


executable openapi-gen
main-is: Main.hs
hs-source-dirs: openApi
ghc-options:
build-depends: base
, auth-service-core
default-language: Haskell2010


test-suite tests
type: exitcode-stdio-1.0
main-is: Main.hs
Expand All @@ -84,6 +95,7 @@ test-suite tests
, bytestring
, containers
, hedgehog
, hspec
, hspec-hedgehog
, lens
, lens-aeson
Expand Down
2 changes: 2 additions & 0 deletions auth-service-core/hie.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ cradle:
stack:
- path: "./src"
component: "auth-service-core:lib"
- path: "./openApi"
component: "api-definition"
14 changes: 14 additions & 0 deletions auth-service-core/openApi/Main.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{-# LANGUAGE LambdaCase #-}

module Main where

import AuthService.OpenAPI
import System.Environment
import System.Exit
import System.IO

main :: IO ()
main = do
getArgs >>= \case
[ path ] -> writeDefinition path
_ -> hPutStrLn stderr "Missing argument: filepath"
7 changes: 5 additions & 2 deletions auth-service-core/src/AuthService/Api.hs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ type SSOLoginAPI = "sso" :> "login"
, Header "Cache-Control" Text
, Header "Pragma" Text
]
NoContent)
SamlLoginRequest)

type SSOEnabledAPI = "sso" :> "enabled" :> Get '[ JSON ] SsoEnabled

type SSOAssertAPI = "sso" :> "assert"
:> Header "X-Instance" InstanceID
Expand Down Expand Up @@ -140,7 +142,8 @@ type ServiceAPI = "service"
-- Interface
--------------------------------------------------------------------------------

type Api = LoginAPI
type Api = SSOEnabledAPI
:<|> LoginAPI
:<|> SSOLoginAPI
:<|> SSOAssertAPI
:<|> CheckTokenAPI
Expand Down
15 changes: 15 additions & 0 deletions auth-service-core/src/AuthService/OpenAPI.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module AuthService.OpenAPI where

import qualified AuthService.Api as API

import qualified Data.Aeson as Aeson
import qualified Data.ByteString.Lazy as BSL
import Data.Data ( Proxy(..) )

import Servant.OpenApi

apiDefinition :: BSL.ByteString
apiDefinition = Aeson.encode $ toOpenApi (Proxy :: Proxy API.Api)

writeDefinition :: FilePath -> IO ()
writeDefinition path = BSL.writeFile path apiDefinition
83 changes: 83 additions & 0 deletions auth-service-core/src/AuthService/OpenAPI/Schema.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
{-# language DerivingVia #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE AllowAmbiguousTypes#-}

-- Generator for OpenAPI schemata

module AuthService.OpenAPI.Schema where

import qualified Data.Aeson as Aeson
import qualified Data.Char as Char
import Data.Data (Typeable, Proxy(..))
import GHC.Generics
import qualified Data.OpenApi.Internal.Schema as OpenApi
import qualified Data.OpenApi.ParamSchema as OpenApi

import Helpers (dropPrefix)
import Data.Aeson (camelTo2)

-- Wrapper for `deriving via`
newtype JSONStruct a = JSONStruct a deriving Show

fieldLabelModifier :: forall a m f. (Generic a, Datatype m
, Rep a ~ M1 D m f)
=> String -> String
fieldLabelModifier =
let (n, ns) = case datatypeName @m undefined of
(c : cs) -> (c, cs)
_ -> error "Empty datatypeName"
in camelTo2 '_' . dropPrefix (Char.toLower n : ns)

-- This *requires* TypeApplications to be called, the `a` type parameter needs
-- to be passed explicitly
options
:: forall a m f. (Generic a, Datatype m
, Rep a ~ M1 D m f)
=> Aeson.Options
options =
Aeson.defaultOptions {Aeson.fieldLabelModifier = fieldLabelModifier @a }


instance (Generic a
, Aeson.GToJSON' Aeson.Value Aeson.Zero f
, Aeson.GToJSON' Aeson.Encoding Aeson.Zero f
, Datatype m, Rep a ~ M1 D m f) => Aeson.ToJSON (JSONStruct a) where


toJSON (JSONStruct x) = Aeson.genericToJSON (options @a) x
toEncoding (JSONStruct x) = Aeson.genericToEncoding (options @a) x


instance (Generic a, Aeson.GFromJSON Aeson.Zero (Rep a)
, Datatype m, Rep a ~ M1 D m f
) => Aeson.FromJSON (JSONStruct a) where
parseJSON x = JSONStruct <$> Aeson.genericParseJSON (options @a) x

schemaOptions
:: forall a m f. (Generic a, Datatype m
, Rep a ~ M1 D m f)
=> OpenApi.SchemaOptions
schemaOptions =
let (n, ns) = case datatypeName @m undefined of
(c : cs) -> (c, cs)
_ -> error "Empty datatypeName"
fieldLabelModifier = dropPrefix (Char.toLower n : ns)
in OpenApi.defaultSchemaOptions {OpenApi.fieldLabelModifier }

instance (Generic a, Typeable a
, Datatype m, Rep a ~ M1 D m f
, OpenApi.GToSchema (Rep a)
)
=> OpenApi.ToSchema (JSONStruct a) where

declareNamedSchema _ = OpenApi.genericDeclareNamedSchema options (Proxy @a)
where
options = OpenApi.defaultSchemaOptions { OpenApi.fieldLabelModifier
= fieldLabelModifier @a
}
Loading

0 comments on commit afbb088

Please sign in to comment.