Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/0-release-notes/pr-2149
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Deploy Brig before Spar.
1 change: 1 addition & 0 deletions changelog.d/2-features/pr-2149
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2nd factor authentication for "create SCIM token" via 6 digit code, sent by email. The feature is disabled per default and can be enabled server or team wide. For details see [/docs/reference/config-options.md#2nd-factor-password-challenge](https://github.com/wireapp/wire-server/blob/develop/docs/reference/config-options.md#2nd-factor-password-challenge)
1 change: 1 addition & 0 deletions changelog.d/5-internal/pr-2149
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Internal endpoint for re-authentication (`GET "/i/users/:uid/reauthenticate"`) in brig has changed in a backwards compatible way. Spar depends on this change for creating a SCIM token with 2nd password challenge.
15 changes: 11 additions & 4 deletions libs/brig-types/src/Brig/Types/Intra.hs
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,12 @@ import Brig.Types.Connection
import Brig.Types.User
import Data.Aeson
import qualified Data.Aeson.KeyMap as KeyMap
import Data.Code as Code
import Data.Id (TeamId, UserId)
import Data.Misc (PlainTextPassword (..))
import qualified Data.Text as Text
import Imports
import Wire.API.User (VerificationAction (..))

-------------------------------------------------------------------------------
-- AccountStatus
Expand Down Expand Up @@ -169,16 +171,21 @@ instance ToJSON UserSet where

-- | Certain operations might require reauth of the user. These are available
-- only for users that have already set a password.
newtype ReAuthUser = ReAuthUser
{reAuthPassword :: Maybe PlainTextPassword}
data ReAuthUser = ReAuthUser
{ reAuthPassword :: Maybe PlainTextPassword,
reAuthCode :: Maybe Code.Value,
reAuthCodeAction :: Maybe VerificationAction
}
deriving (Eq, Show, Generic)

instance FromJSON ReAuthUser where
parseJSON = withObject "reauth-user" $ \o ->
ReAuthUser <$> o .:? "password"
ReAuthUser <$> o .:? "password" <*> o .:? "verification_code" <*> o .:? "action"

instance ToJSON ReAuthUser where
toJSON ru =
object
[ "password" .= reAuthPassword ru
[ "password" .= reAuthPassword ru,
"verification_code" .= reAuthCode ru,
"action" .= reAuthCodeAction ru
]
2 changes: 1 addition & 1 deletion libs/brig-types/test/unit/Test/Brig/Types/User.hs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ instance Arbitrary RichInfoUpdate where
arbitrary = RichInfoUpdate <$> arbitrary

instance Arbitrary ReAuthUser where
arbitrary = ReAuthUser <$> arbitrary
arbitrary = ReAuthUser <$> arbitrary <*> arbitrary <*> arbitrary

instance Arbitrary NewUserScimInvitation where
arbitrary = NewUserScimInvitation <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
2 changes: 2 additions & 0 deletions libs/wire-api/src/Wire/API/ErrorDescription.hs
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,8 @@ type PasswordAuthenticationFailed = ErrorDescription 403 "password-authenticatio

type CodeAuthenticationFailed = ErrorDescription 403 "code-authentication-failed" "Code authentication failed."

type CodeAuthenticationRequired = ErrorDescription 403 "code-authentication-required" "Code authentication is required."

type MLSProtocolError = ErrorDescription 400 "mls-protocol-error" "MLS protocol error"

mlsProtocolError :: Text -> MLSProtocolError
Expand Down
11 changes: 10 additions & 1 deletion libs/wire-api/src/Wire/API/Routes/Internal/Brig.hs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ module Wire.API.Routes.Internal.Brig
where

import Control.Lens ((.~))
import qualified Data.Code as Code
import Data.Id as Id
import Data.Swagger (HasInfo (info), HasTitle (title), Swagger)
import Imports hiding (head)
Expand Down Expand Up @@ -152,9 +153,17 @@ type GetClientByKeyPackageRef =
]
(Maybe ClientIdentity)

type GetVerificationCode =
Summary "Get verification code for a given email and action"
:> "users"
:> Capture "uid" UserId
:> "verification-code"
:> Capture "action" VerificationAction
:> Get '[Servant.JSON] (Maybe Code.Value)

type API =
"i"
:> (EJPD_API :<|> AccountAPI :<|> MLSAPI)
:> (EJPD_API :<|> AccountAPI :<|> MLSAPI :<|> GetVerificationCode)

type SwaggerDocsAPI = "api" :> "internal" :> SwaggerSchemaUI "swagger-ui" "swagger.json"

Expand Down
3 changes: 2 additions & 1 deletion libs/wire-api/src/Wire/API/Routes/Public/Spar.hs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import Web.Scim.Capabilities.MetaSchema as Scim.Meta
import Web.Scim.Class.Auth as Scim.Auth
import Web.Scim.Class.User as Scim.User
import Wire.API.Cookie
import Wire.API.ErrorDescription (CanThrow, CodeAuthenticationFailed, PasswordAuthenticationFailed)
import Wire.API.ErrorDescription
import Wire.API.Routes.Public
import Wire.API.User.IdentityProvider
import Wire.API.User.Saml
Expand Down Expand Up @@ -207,6 +207,7 @@ type APIScim =
:<|> "auth-tokens"
:> CanThrow PasswordAuthenticationFailed
:> CanThrow CodeAuthenticationFailed
:> CanThrow CodeAuthenticationRequired
:> APIScimToken

type ScimSiteAPI tag = ToServantApi (ScimSite tag)
Expand Down
39 changes: 35 additions & 4 deletions libs/wire-api/src/Wire/API/User.hs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE StrictData #-}

-- This file is part of the Wire Server implementation.
--
Expand Down Expand Up @@ -109,11 +109,13 @@ import Control.Error.Safe (rightMay)
import Control.Lens (over, (.~), (?~))
import Data.Aeson (FromJSON (..), ToJSON (..))
import qualified Data.Aeson.Types as A
import qualified Data.Attoparsec.ByteString as Parser
import Data.ByteString.Conversion
import qualified Data.CaseInsensitive as CI
import qualified Data.Code as Code
import qualified Data.Currency as Currency
import Data.Domain (Domain (Domain))
import Data.Either.Extra (maybeToEither)
import Data.Handle (Handle)
import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
import Data.Id
Expand All @@ -124,17 +126,20 @@ import Data.Qualified
import Data.Range
import Data.SOP
import Data.Schema
import Data.String.Conversions (cs)
import qualified Data.Swagger as S
import qualified Data.Swagger.Build.Api as Doc
import qualified Data.Text as T
import Data.Text.Ascii
import qualified Data.Text.Encoding as T
import Data.UUID (UUID, nil)
import qualified Data.UUID as UUID
import Deriving.Swagger
import GHC.TypeLits (KnownNat, Nat)
import qualified Generics.SOP as GSOP
import Imports
import qualified SAML2.WebSSO as SAML
import Servant (type (.++))
import Servant (FromHttpApiData (..), ToHttpApiData (..), type (.++))
import qualified Test.QuickCheck as QC
import qualified Web.Cookie as Web
import Wire.API.Arbitrary (Arbitrary (arbitrary), GenericUniform (..))
Expand Down Expand Up @@ -1195,7 +1200,7 @@ instance S.ToSchema ListUsersQuery where
-----------------------------------------------------------------------------
-- SndFactorPasswordChallenge

data VerificationAction = GenerateScimToken | Login
data VerificationAction = CreateScimToken | Login
deriving stock (Eq, Show, Enum, Bounded, Generic)
deriving (Arbitrary) via (GenericUniform VerificationAction)
deriving (FromJSON, ToJSON, S.ToSchema) via (Schema VerificationAction)
Expand All @@ -1204,10 +1209,36 @@ instance ToSchema VerificationAction where
schema =
enum @Text "VerificationAction" $
mconcat
[ element "generate_scim_token" GenerateScimToken,
[ element "create_scim_token" CreateScimToken,
element "login" Login
]

instance ToByteString VerificationAction where
builder CreateScimToken = "create_scim_token"
builder Login = "login"

instance FromByteString VerificationAction where
parser =
Parser.takeByteString >>= \b ->
case T.decodeUtf8' b of
Right "login" -> pure Login
Right "create_scim_token" -> pure CreateScimToken
Right t -> fail $ "Invalid VerificationAction: " <> T.unpack t
Left e -> fail $ "Invalid VerificationAction: " <> show e

instance S.ToParamSchema VerificationAction where
toParamSchema _ =
mempty
{ S._paramSchemaType = Just S.SwaggerString,
S._paramSchemaEnum = Just (A.String . toQueryParam <$> [(minBound :: VerificationAction) ..])
}

instance FromHttpApiData VerificationAction where
parseUrlPiece = maybeToEither "Invalid verification action" . fromByteString . cs

instance ToHttpApiData VerificationAction where
toQueryParam a = cs (toByteString' a)

data SendVerificationCode = SendVerificationCode
{ svcAction :: VerificationAction,
svcEmail :: Email
Expand Down
4 changes: 2 additions & 2 deletions libs/wire-api/src/Wire/API/User/Scim.hs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import qualified Data.Binary.Builder as BB
import Data.ByteArray.Encoding (Base (..), convertToBase)
import Data.ByteString.Conversion (FromByteString (..), ToByteString (..))
import qualified Data.CaseInsensitive as CI
import Data.Code as Code
import Data.Handle (Handle)
import Data.Id (ScimTokenId, TeamId, UserId)
import Data.Json.Util ((#))
Expand Down Expand Up @@ -84,7 +85,6 @@ import qualified Web.Scim.Schema.Schema as Scim
import qualified Web.Scim.Schema.User as Scim
import qualified Web.Scim.Schema.User as Scim.User
import Wire.API.Arbitrary (Arbitrary, GenericUniform (..))
import Wire.API.User.Activation
import Wire.API.User.Identity (Email)
import Wire.API.User.Profile as BT
import qualified Wire.API.User.RichInfo as RI
Expand Down Expand Up @@ -369,7 +369,7 @@ data CreateScimToken = CreateScimToken
-- | User password, which we ask for because creating a token is a "powerful" operation
createScimTokenPassword :: !(Maybe PlainTextPassword),
-- | User code (sent by email), for 2nd factor to 'createScimTokenPassword'
createScimTokenCode :: !(Maybe ActivationCode)
createScimTokenCode :: !(Maybe Code.Value)
}
deriving (Eq, Show, Generic)
deriving (Arbitrary) via (GenericUniform CreateScimToken)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ module Test.Wire.API.Golden.Generated.VerificationAction_user where
import Wire.API.User (VerificationAction (..))

testObject_VerificationAction_user_1 :: VerificationAction
testObject_VerificationAction_user_1 = GenerateScimToken
testObject_VerificationAction_user_1 = CreateScimToken

testObject_VerificationAction_user_2 :: VerificationAction
testObject_VerificationAction_user_2 = Login
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,19 @@

module Test.Wire.API.Golden.Manual.CreateScimToken where

import Data.Code
import Data.Misc (PlainTextPassword (PlainTextPassword))
import Data.Range (unsafeRange)
import Data.Text.Ascii (AsciiChars (validate))
import Imports
import Wire.API.User.Activation (ActivationCode (ActivationCode, fromActivationCode))
import Imports (Maybe (Just, Nothing), fromRight, undefined)
import Wire.API.User.Scim (CreateScimToken (..))

testObject_CreateScimToken_1 :: CreateScimToken
testObject_CreateScimToken_1 =
CreateScimToken
"description"
(Just (PlainTextPassword "very-geheim"))
(Just ((ActivationCode {fromActivationCode = fromRight undefined (validate "123456")})))
(Just (Value {asciiValue = unsafeRange (fromRight undefined (validate "123456"))}))

testObject_CreateScimToken_2 :: CreateScimToken
testObject_CreateScimToken_2 =
Expand All @@ -42,7 +43,7 @@ testObject_CreateScimToken_3 =
CreateScimToken
"description3"
Nothing
(Just ((ActivationCode {fromActivationCode = fromRight undefined (validate "654321")})))
(Just (Value {asciiValue = unsafeRange (fromRight undefined (validate "654321"))}))

testObject_CreateScimToken_4 :: CreateScimToken
testObject_CreateScimToken_4 =
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
"generate_scim_token"
"create_scim_token"
Loading