mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-02-15 20:33:26 +00:00
Fix OTP authentication for external accounts
This commit is contained in:
parent
8158e36d40
commit
468ba90158
@ -9,7 +9,9 @@ package docspell.oidc
|
||||
import cats.data.{Kleisli, OptionT}
|
||||
import cats.effect._
|
||||
import cats.implicits._
|
||||
|
||||
import docspell.common._
|
||||
|
||||
import org.http4s.HttpRoutes
|
||||
import org.http4s._
|
||||
import org.http4s.client.Client
|
||||
|
@ -78,8 +78,8 @@ object Config {
|
||||
object FullTextSearch {}
|
||||
|
||||
final case class OpenIdConfig(
|
||||
enabled: Boolean,
|
||||
display: String,
|
||||
enabled: Boolean,
|
||||
display: String,
|
||||
collectiveKey: OpenId.UserInfo.Extractor,
|
||||
userKey: String,
|
||||
provider: ProviderConfig
|
||||
|
@ -54,7 +54,9 @@ object OpenId {
|
||||
|
||||
extractColl match {
|
||||
case ExtractResult.Failure(message) =>
|
||||
logger.warn(s"Can't retrieve user data using collective-key=${cfg.collectiveKey.asString}: $message") *>
|
||||
logger.warn(
|
||||
s"Can't retrieve user data using collective-key=${cfg.collectiveKey.asString}: $message"
|
||||
) *>
|
||||
TemporaryRedirect(location)
|
||||
|
||||
case ExtractResult.Account(accountId) =>
|
||||
@ -63,7 +65,9 @@ object OpenId {
|
||||
case ExtractResult.Identifier(coll) =>
|
||||
Extractor.Lookup(cfg.userKey).find(userJson) match {
|
||||
case ExtractResult.Failure(message) =>
|
||||
logger.warn(s"Can't retrieve user data using user-key=${cfg.userKey}: $message") *>
|
||||
logger.warn(
|
||||
s"Can't retrieve user data using user-key=${cfg.userKey}: $message"
|
||||
) *>
|
||||
TemporaryRedirect(location)
|
||||
|
||||
case ExtractResult.Identifier(name) =>
|
||||
@ -144,7 +148,15 @@ object OpenId {
|
||||
login <- backend.login.loginExternal(config.auth)(accountId)
|
||||
resp <- login match {
|
||||
case Login.Result.Ok(session, _) =>
|
||||
TemporaryRedirect(location)
|
||||
val loc =
|
||||
if (session.requireSecondFactor)
|
||||
location.copy(uri =
|
||||
location.uri
|
||||
.withQueryParam("openid", "2")
|
||||
.withQueryParam("auth", session.asString)
|
||||
)
|
||||
else location
|
||||
TemporaryRedirect(loc)
|
||||
.map(_.addCookie(CookieData(session).asCookie(baseUrl)))
|
||||
|
||||
case failed =>
|
||||
|
@ -9,6 +9,7 @@ package docspell.restserver.webapp
|
||||
import docspell.backend.signup.{Config => SignupConfig}
|
||||
import docspell.common.{Ident, LenientUri}
|
||||
import docspell.restserver.{BuildInfo, Config}
|
||||
|
||||
import io.circe._
|
||||
import io.circe.generic.semiauto._
|
||||
import yamusca.implicits._
|
||||
|
@ -83,10 +83,7 @@ init key url flags_ settings =
|
||||
Page.CollectiveSettings.Data.init flags
|
||||
|
||||
( loginm, loginc ) =
|
||||
Page.Login.Data.init flags
|
||||
(Page.loginPageReferrer page
|
||||
|> Tuple.second
|
||||
)
|
||||
Page.Login.Data.init flags (Page.loginPageReferrer page)
|
||||
|
||||
homeViewMode =
|
||||
if settings.searchMenuVisible then
|
||||
|
@ -158,7 +158,7 @@ updateWithSub msg model =
|
||||
|
||||
LogoutResp _ ->
|
||||
( { model | loginModel = Page.Login.Data.emptyModel }
|
||||
, Page.goto (LoginPage ( Nothing, False ))
|
||||
, Page.goto (LoginPage Page.emptyLoginData)
|
||||
, Sub.none
|
||||
)
|
||||
|
||||
|
@ -6,7 +6,9 @@
|
||||
|
||||
|
||||
module Page exposing
|
||||
( Page(..)
|
||||
( LoginData
|
||||
, Page(..)
|
||||
, emptyLoginData
|
||||
, fromUrl
|
||||
, goto
|
||||
, hasSidebar
|
||||
@ -31,9 +33,24 @@ import Url.Parser.Query as Query
|
||||
import Util.Maybe
|
||||
|
||||
|
||||
type alias LoginData =
|
||||
{ referrer : Maybe Page
|
||||
, session : Maybe String
|
||||
, openid : Int
|
||||
}
|
||||
|
||||
|
||||
emptyLoginData : LoginData
|
||||
emptyLoginData =
|
||||
{ referrer = Nothing
|
||||
, session = Nothing
|
||||
, openid = 0
|
||||
}
|
||||
|
||||
|
||||
type Page
|
||||
= HomePage
|
||||
| LoginPage ( Maybe Page, Bool )
|
||||
| LoginPage LoginData
|
||||
| ManageDataPage
|
||||
| CollectiveSettingPage
|
||||
| UserSettingPage
|
||||
@ -99,10 +116,10 @@ loginPage : Page -> Page
|
||||
loginPage p =
|
||||
case p of
|
||||
LoginPage _ ->
|
||||
LoginPage ( Nothing, False )
|
||||
LoginPage emptyLoginData
|
||||
|
||||
_ ->
|
||||
LoginPage ( Just p, False )
|
||||
LoginPage { emptyLoginData | referrer = Just p }
|
||||
|
||||
|
||||
pageName : Page -> String
|
||||
@ -144,14 +161,14 @@ pageName page =
|
||||
"Item"
|
||||
|
||||
|
||||
loginPageReferrer : Page -> ( Maybe Page, Bool )
|
||||
loginPageReferrer : Page -> LoginData
|
||||
loginPageReferrer page =
|
||||
case page of
|
||||
LoginPage ( r, flag ) ->
|
||||
( r, flag )
|
||||
LoginPage data ->
|
||||
data
|
||||
|
||||
_ ->
|
||||
( Nothing, False )
|
||||
emptyLoginData
|
||||
|
||||
|
||||
uploadId : Page -> Maybe String
|
||||
@ -170,8 +187,8 @@ pageToString page =
|
||||
HomePage ->
|
||||
"/app/home"
|
||||
|
||||
LoginPage ( referer, _ ) ->
|
||||
case referer of
|
||||
LoginPage data ->
|
||||
case data.referrer of
|
||||
Just (LoginPage _) ->
|
||||
"/app/login"
|
||||
|
||||
@ -280,14 +297,19 @@ fromString str =
|
||||
fromUrl url
|
||||
|
||||
|
||||
loginPageOAuthQuery : Query.Parser Bool
|
||||
loginPageOAuthQuery : Query.Parser Int
|
||||
loginPageOAuthQuery =
|
||||
Query.map Util.Maybe.nonEmpty (Query.string "openid")
|
||||
Query.map (Maybe.withDefault 0) (Query.int "openid")
|
||||
|
||||
|
||||
loginPageParser : Query.Parser ( Maybe Page, Bool )
|
||||
loginPageSessionQuery : Query.Parser (Maybe String)
|
||||
loginPageSessionQuery =
|
||||
Query.string "auth"
|
||||
|
||||
|
||||
loginPageParser : Query.Parser LoginData
|
||||
loginPageParser =
|
||||
Query.map2 Tuple.pair pageQuery loginPageOAuthQuery
|
||||
Query.map3 LoginData pageQuery loginPageSessionQuery loginPageOAuthQuery
|
||||
|
||||
|
||||
pageQuery : Query.Parser (Maybe Page)
|
||||
|
@ -18,7 +18,7 @@ import Api
|
||||
import Api.Model.AuthResult exposing (AuthResult)
|
||||
import Data.Flags exposing (Flags)
|
||||
import Http
|
||||
import Page exposing (Page(..))
|
||||
import Page exposing (LoginData, Page(..))
|
||||
|
||||
|
||||
type alias Model =
|
||||
@ -40,7 +40,7 @@ type FormState
|
||||
|
||||
type AuthStep
|
||||
= StepLogin
|
||||
| StepOtp AuthResult
|
||||
| StepOtp String
|
||||
|
||||
|
||||
emptyModel : Model
|
||||
@ -54,11 +54,11 @@ emptyModel =
|
||||
}
|
||||
|
||||
|
||||
init : Flags -> Bool -> ( Model, Cmd Msg )
|
||||
init flags oauth =
|
||||
init : Flags -> LoginData -> ( Model, Cmd Msg )
|
||||
init flags ld =
|
||||
let
|
||||
cmd =
|
||||
if oauth then
|
||||
if ld.openid > 0 then
|
||||
Api.loginSession flags AuthResp
|
||||
|
||||
else
|
||||
@ -74,4 +74,4 @@ type Msg
|
||||
| Authenticate
|
||||
| AuthResp (Result Http.Error AuthResult)
|
||||
| SetOtp String
|
||||
| AuthOtp AuthResult
|
||||
| AuthOtp String
|
||||
|
@ -10,13 +10,13 @@ module Page.Login.Update exposing (update)
|
||||
import Api
|
||||
import Api.Model.AuthResult exposing (AuthResult)
|
||||
import Data.Flags exposing (Flags)
|
||||
import Page exposing (Page(..))
|
||||
import Page exposing (LoginData, Page(..))
|
||||
import Page.Login.Data exposing (..)
|
||||
import Ports
|
||||
|
||||
|
||||
update : ( Maybe Page, Bool ) -> Flags -> Msg -> Model -> ( Model, Cmd Msg, Maybe AuthResult )
|
||||
update ( referrer, oauth ) flags msg model =
|
||||
update : LoginData -> Flags -> Msg -> Model -> ( Model, Cmd Msg, Maybe AuthResult )
|
||||
update loginData flags msg model =
|
||||
case msg of
|
||||
SetUsername str ->
|
||||
( { model | username = str }, Cmd.none, Nothing )
|
||||
@ -40,11 +40,11 @@ update ( referrer, oauth ) flags msg model =
|
||||
in
|
||||
( model, Api.login flags userPass AuthResp, Nothing )
|
||||
|
||||
AuthOtp acc ->
|
||||
AuthOtp token ->
|
||||
let
|
||||
sf =
|
||||
{ rememberMe = model.rememberMe
|
||||
, token = Maybe.withDefault "" acc.token
|
||||
, token = token
|
||||
, otp = model.otp
|
||||
}
|
||||
in
|
||||
@ -53,7 +53,7 @@ update ( referrer, oauth ) flags msg model =
|
||||
AuthResp (Ok lr) ->
|
||||
let
|
||||
gotoRef =
|
||||
Maybe.withDefault HomePage referrer |> Page.goto
|
||||
Maybe.withDefault HomePage loginData.referrer |> Page.goto
|
||||
in
|
||||
if lr.success && not lr.requireSecondFactor then
|
||||
( { model | formState = AuthSuccess lr, password = "" }
|
||||
@ -62,7 +62,11 @@ update ( referrer, oauth ) flags msg model =
|
||||
)
|
||||
|
||||
else if lr.success && lr.requireSecondFactor then
|
||||
( { model | formState = FormInitial, authStep = StepOtp lr, password = "" }
|
||||
( { model
|
||||
| formState = FormInitial
|
||||
, authStep = StepOtp <| Maybe.withDefault "" lr.token
|
||||
, password = ""
|
||||
}
|
||||
, Cmd.none
|
||||
, Nothing
|
||||
)
|
||||
@ -77,11 +81,22 @@ update ( referrer, oauth ) flags msg model =
|
||||
let
|
||||
empty =
|
||||
Api.Model.AuthResult.empty
|
||||
|
||||
session =
|
||||
Maybe.withDefault "" loginData.session
|
||||
in
|
||||
( { model | password = "", formState = HttpError err }
|
||||
, Ports.removeAccount ()
|
||||
, Just empty
|
||||
)
|
||||
-- A value of 2 indicates that TOTP is required
|
||||
if loginData.openid == 2 then
|
||||
( { model | formState = FormInitial, authStep = StepOtp session, password = "" }
|
||||
, Cmd.none
|
||||
, Nothing
|
||||
)
|
||||
|
||||
else
|
||||
( { model | password = "", formState = HttpError err }
|
||||
, Ports.removeAccount ()
|
||||
, Just empty
|
||||
)
|
||||
|
||||
|
||||
setAccount : AuthResult -> Cmd msg
|
||||
|
@ -104,11 +104,11 @@ openIdLinks texts flags =
|
||||
]
|
||||
|
||||
|
||||
otpForm : Texts -> Flags -> Model -> AuthResult -> Html Msg
|
||||
otpForm texts flags model acc =
|
||||
otpForm : Texts -> Flags -> Model -> String -> Html Msg
|
||||
otpForm texts flags model token =
|
||||
Html.form
|
||||
[ action "#"
|
||||
, onSubmit (AuthOtp acc)
|
||||
, onSubmit (AuthOtp token)
|
||||
, autocomplete False
|
||||
]
|
||||
[ div [ class "flex flex-col mt-6" ]
|
||||
|
@ -97,7 +97,7 @@ update flags msg model =
|
||||
|
||||
cmd =
|
||||
if r.success then
|
||||
Page.goto (LoginPage ( Nothing, False ))
|
||||
Page.goto (LoginPage Page.emptyLoginData)
|
||||
|
||||
else
|
||||
Cmd.none
|
||||
|
@ -232,7 +232,7 @@ viewContent texts flags _ model =
|
||||
[ text texts.alreadySignedUp
|
||||
]
|
||||
, a
|
||||
[ Page.href (LoginPage ( Nothing, False ))
|
||||
[ Page.href (LoginPage Page.emptyLoginData)
|
||||
, class ("ml-2" ++ S.link)
|
||||
]
|
||||
[ i [ class "fa fa-user-plus mr-1" ] []
|
||||
|
Loading…
Reference in New Issue
Block a user