commit
acd4a07d5e
|
@ -4,3 +4,7 @@ elm-stuff
|
||||||
repl-temp-*
|
repl-temp-*
|
||||||
# VScode settings
|
# VScode settings
|
||||||
.vscode/
|
.vscode/
|
||||||
|
|
||||||
|
# Elm output
|
||||||
|
index.html
|
||||||
|
elm.js
|
||||||
|
|
|
@ -190,6 +190,7 @@ loginWithUsernameAndPasswordV1 { username, password } =
|
||||||
, refresh = out.refreshToken
|
, refresh = out.refreshToken
|
||||||
, value = out.accessToken
|
, value = out.accessToken
|
||||||
}
|
}
|
||||||
|
, E.RemovePasswordIfNecessary
|
||||||
, out.user
|
, out.user
|
||||||
|> Maybe.map (V.SetUser >> E.ContentUpdate)
|
|> Maybe.map (V.SetUser >> E.ContentUpdate)
|
||||||
|> E.Optional
|
|> E.Optional
|
||||||
|
@ -231,6 +232,7 @@ loginWithUsernameAndPasswordV2 { deviceId, initialDeviceDisplayName, username, p
|
||||||
, refresh = Nothing
|
, refresh = Nothing
|
||||||
, value = out.accessToken
|
, value = out.accessToken
|
||||||
}
|
}
|
||||||
|
, E.RemovePasswordIfNecessary
|
||||||
, out.user
|
, out.user
|
||||||
|> Maybe.map (V.SetUser >> E.ContentUpdate)
|
|> Maybe.map (V.SetUser >> E.ContentUpdate)
|
||||||
|> E.Optional
|
|> E.Optional
|
||||||
|
@ -282,6 +284,7 @@ loginWithUsernameAndPasswordV3 { deviceId, initialDeviceDisplayName, username, p
|
||||||
, refresh = Nothing
|
, refresh = Nothing
|
||||||
, value = out.accessToken
|
, value = out.accessToken
|
||||||
}
|
}
|
||||||
|
, E.RemovePasswordIfNecessary
|
||||||
, out.user
|
, out.user
|
||||||
|> Maybe.map (V.SetUser >> E.ContentUpdate)
|
|> Maybe.map (V.SetUser >> E.ContentUpdate)
|
||||||
|> E.Optional
|
|> E.Optional
|
||||||
|
@ -333,6 +336,7 @@ loginWithUsernameAndPasswordV4 { deviceId, initialDeviceDisplayName, username, p
|
||||||
, refresh = Nothing
|
, refresh = Nothing
|
||||||
, value = out.accessToken
|
, value = out.accessToken
|
||||||
}
|
}
|
||||||
|
, E.RemovePasswordIfNecessary
|
||||||
, out.user
|
, out.user
|
||||||
|> Maybe.map (V.SetUser >> E.ContentUpdate)
|
|> Maybe.map (V.SetUser >> E.ContentUpdate)
|
||||||
|> E.Optional
|
|> E.Optional
|
||||||
|
@ -388,6 +392,7 @@ loginWithUsernameAndPasswordV5 { deviceId, initialDeviceDisplayName, username, p
|
||||||
, refresh = Nothing
|
, refresh = Nothing
|
||||||
, value = out.accessToken
|
, value = out.accessToken
|
||||||
}
|
}
|
||||||
|
, E.RemovePasswordIfNecessary
|
||||||
, out.user
|
, out.user
|
||||||
|> Maybe.map (V.SetUser >> E.ContentUpdate)
|
|> Maybe.map (V.SetUser >> E.ContentUpdate)
|
||||||
|> E.Optional
|
|> E.Optional
|
||||||
|
@ -444,6 +449,7 @@ loginWithUsernameAndPasswordV6 { deviceId, enableRefreshToken, initialDeviceDisp
|
||||||
, refresh = out.refreshToken
|
, refresh = out.refreshToken
|
||||||
, value = out.accessToken
|
, value = out.accessToken
|
||||||
}
|
}
|
||||||
|
, E.RemovePasswordIfNecessary
|
||||||
, out.user
|
, out.user
|
||||||
|> Maybe.map (V.SetUser >> E.ContentUpdate)
|
|> Maybe.map (V.SetUser >> E.ContentUpdate)
|
||||||
|> E.Optional
|
|> E.Optional
|
||||||
|
@ -500,6 +506,7 @@ loginWithUsernameAndPasswordV7 { deviceId, enableRefreshToken, initialDeviceDisp
|
||||||
, refresh = out.refreshToken
|
, refresh = out.refreshToken
|
||||||
, value = out.accessToken
|
, value = out.accessToken
|
||||||
}
|
}
|
||||||
|
, E.RemovePasswordIfNecessary
|
||||||
, E.ContentUpdate (V.SetUser out.user)
|
, E.ContentUpdate (V.SetUser out.user)
|
||||||
, out.wellKnown
|
, out.wellKnown
|
||||||
|> Maybe.map (.homeserver >> .baseUrl)
|
|> Maybe.map (.homeserver >> .baseUrl)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
module Internal.Config.Default exposing
|
module Internal.Config.Default exposing
|
||||||
( currentVersion, deviceName
|
( currentVersion, deviceName
|
||||||
, syncTime
|
, syncTime
|
||||||
|
, removePasswordOnLogin
|
||||||
)
|
)
|
||||||
|
|
||||||
{-| This module hosts all default settings and configurations that the Vault
|
{-| This module hosts all default settings and configurations that the Vault
|
||||||
|
@ -16,6 +17,11 @@ will assume until overriden by the user.
|
||||||
|
|
||||||
@docs syncTime
|
@docs syncTime
|
||||||
|
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
@docs removePasswordOnLogin
|
||||||
|
|
||||||
-}
|
-}
|
||||||
|
|
||||||
|
|
||||||
|
@ -52,3 +58,13 @@ The value is in miliseconds, so it is set at 30,000.
|
||||||
syncTime : Int
|
syncTime : Int
|
||||||
syncTime =
|
syncTime =
|
||||||
30 * 1000
|
30 * 1000
|
||||||
|
|
||||||
|
|
||||||
|
{-| Once the Matrix API has logged in successfully, it does not need to remember
|
||||||
|
the user's password. However, to keep the Vault logged in automatically, one may
|
||||||
|
choose to remember the password in order to get a new access token when an old
|
||||||
|
access token has expired.
|
||||||
|
-}
|
||||||
|
removePasswordOnLogin : Bool
|
||||||
|
removePasswordOnLogin =
|
||||||
|
True
|
||||||
|
|
|
@ -321,6 +321,7 @@ fields :
|
||||||
, settings :
|
, settings :
|
||||||
{ currentVersion : Desc
|
{ currentVersion : Desc
|
||||||
, deviceName : Desc
|
, deviceName : Desc
|
||||||
|
, removePasswordOnLogin : Desc
|
||||||
, syncTime : Desc
|
, syncTime : Desc
|
||||||
}
|
}
|
||||||
, timeline :
|
, timeline :
|
||||||
|
@ -501,6 +502,9 @@ fields =
|
||||||
, deviceName =
|
, deviceName =
|
||||||
[ "Indicates the device name that is communicated to the Matrix API."
|
[ "Indicates the device name that is communicated to the Matrix API."
|
||||||
]
|
]
|
||||||
|
, removePasswordOnLogin =
|
||||||
|
[ "Remove the password as soon as a valid access token has been received."
|
||||||
|
]
|
||||||
, syncTime =
|
, syncTime =
|
||||||
[ "Indicates the frequency in miliseconds with which the Elm SDK should long-poll the /sync endpoint."
|
[ "Indicates the frequency in miliseconds with which the Elm SDK should long-poll the /sync endpoint."
|
||||||
]
|
]
|
||||||
|
|
|
@ -78,6 +78,7 @@ type EnvelopeUpdate a
|
||||||
| More (List (EnvelopeUpdate a))
|
| More (List (EnvelopeUpdate a))
|
||||||
| Optional (Maybe (EnvelopeUpdate a))
|
| Optional (Maybe (EnvelopeUpdate a))
|
||||||
| RemoveAccessToken String
|
| RemoveAccessToken String
|
||||||
|
| RemovePasswordIfNecessary
|
||||||
| SetAccessToken AccessToken
|
| SetAccessToken AccessToken
|
||||||
| SetBaseUrl String
|
| SetBaseUrl String
|
||||||
| SetDeviceId String
|
| SetDeviceId String
|
||||||
|
@ -311,6 +312,13 @@ update updateContent eu ({ context } as data) =
|
||||||
RemoveAccessToken token ->
|
RemoveAccessToken token ->
|
||||||
{ data | context = { context | accessTokens = Hashdict.removeKey token context.accessTokens } }
|
{ data | context = { context | accessTokens = Hashdict.removeKey token context.accessTokens } }
|
||||||
|
|
||||||
|
RemovePasswordIfNecessary ->
|
||||||
|
if data.settings.removePasswordOnLogin then
|
||||||
|
{ data | context = { context | password = Nothing } }
|
||||||
|
|
||||||
|
else
|
||||||
|
data
|
||||||
|
|
||||||
SetAccessToken a ->
|
SetAccessToken a ->
|
||||||
{ data | context = { context | accessTokens = Hashdict.insert a context.accessTokens } }
|
{ data | context = { context | accessTokens = Hashdict.insert a context.accessTokens } }
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ behave under the user's preferred settings.
|
||||||
type alias Settings =
|
type alias Settings =
|
||||||
{ currentVersion : String
|
{ currentVersion : String
|
||||||
, deviceName : String
|
, deviceName : String
|
||||||
|
, removePasswordOnLogin : Bool
|
||||||
, syncTime : Int
|
, syncTime : Int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +44,7 @@ type alias Settings =
|
||||||
-}
|
-}
|
||||||
coder : Json.Coder Settings
|
coder : Json.Coder Settings
|
||||||
coder =
|
coder =
|
||||||
Json.object3
|
Json.object4
|
||||||
{ name = Text.docs.settings.name
|
{ name = Text.docs.settings.name
|
||||||
, description = Text.docs.settings.description
|
, description = Text.docs.settings.description
|
||||||
, init = Settings
|
, init = Settings
|
||||||
|
@ -66,6 +67,21 @@ coder =
|
||||||
, defaultToString = identity
|
, defaultToString = identity
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
(Json.field.optional.withDefault
|
||||||
|
{ fieldName = "removePasswordOnLogin"
|
||||||
|
, toField = .removePasswordOnLogin
|
||||||
|
, description = Text.fields.settings.removePasswordOnLogin
|
||||||
|
, coder = Json.bool
|
||||||
|
, default = Tuple.pair Default.removePasswordOnLogin []
|
||||||
|
, defaultToString =
|
||||||
|
\b ->
|
||||||
|
if b then
|
||||||
|
"true"
|
||||||
|
|
||||||
|
else
|
||||||
|
"false"
|
||||||
|
}
|
||||||
|
)
|
||||||
(Json.field.optional.withDefault
|
(Json.field.optional.withDefault
|
||||||
{ fieldName = "syncTime"
|
{ fieldName = "syncTime"
|
||||||
, toField = .syncTime
|
, toField = .syncTime
|
||||||
|
@ -97,5 +113,6 @@ init : Settings
|
||||||
init =
|
init =
|
||||||
{ currentVersion = Default.currentVersion
|
{ currentVersion = Default.currentVersion
|
||||||
, deviceName = Default.deviceName
|
, deviceName = Default.deviceName
|
||||||
|
, removePasswordOnLogin = Default.removePasswordOnLogin
|
||||||
, syncTime = Default.syncTime
|
, syncTime = Default.syncTime
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ import Internal.Values.User as User exposing (User)
|
||||||
type alias Vault =
|
type alias Vault =
|
||||||
{ accountData : Dict String Json.Value
|
{ accountData : Dict String Json.Value
|
||||||
, rooms : Hashdict Room
|
, rooms : Hashdict Room
|
||||||
, user : User
|
, user : Maybe User
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ coder =
|
||||||
, coder = Hashdict.coder .roomId Room.coder
|
, coder = Hashdict.coder .roomId Room.coder
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
(Json.field.required
|
(Json.field.optional.value
|
||||||
{ fieldName = "user"
|
{ fieldName = "user"
|
||||||
, toField = .user
|
, toField = .user
|
||||||
, description = Text.fields.vault.user
|
, description = Text.fields.vault.user
|
||||||
|
@ -106,11 +106,11 @@ getAccountData key vault =
|
||||||
|
|
||||||
{-| Initiate a new Vault type.
|
{-| Initiate a new Vault type.
|
||||||
-}
|
-}
|
||||||
init : User -> Vault
|
init : Maybe User -> Vault
|
||||||
init user =
|
init mUser =
|
||||||
{ accountData = Dict.empty
|
{ accountData = Dict.empty
|
||||||
, rooms = Hashdict.empty .roomId
|
, rooms = Hashdict.empty .roomId
|
||||||
, user = user
|
, user = mUser
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -156,4 +156,4 @@ update vu vault =
|
||||||
setAccountData key value vault
|
setAccountData key value vault
|
||||||
|
|
||||||
SetUser user ->
|
SetUser user ->
|
||||||
{ vault | user = user }
|
{ vault | user = Just user }
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
module Matrix exposing
|
module Matrix exposing
|
||||||
( Vault, fromUserId
|
( Vault, fromUserId, fromUsername
|
||||||
, VaultUpdate, update
|
, VaultUpdate, update
|
||||||
, addAccessToken, sendMessageEvent
|
, addAccessToken, sendMessageEvent
|
||||||
)
|
)
|
||||||
|
@ -19,7 +19,7 @@ support a monolithic public registry. (:
|
||||||
|
|
||||||
## Vault
|
## Vault
|
||||||
|
|
||||||
@docs Vault, fromUserId
|
@docs Vault, fromUserId, fromUsername
|
||||||
|
|
||||||
|
|
||||||
## Keeping the Vault up-to-date
|
## Keeping the Vault up-to-date
|
||||||
|
@ -57,6 +57,9 @@ type alias VaultUpdate =
|
||||||
Types.VaultUpdate
|
Types.VaultUpdate
|
||||||
|
|
||||||
|
|
||||||
|
{-| Adds a custom access token to the Vault. This can be done if no password is
|
||||||
|
provided or known.
|
||||||
|
-}
|
||||||
addAccessToken : String -> Vault -> Vault
|
addAccessToken : String -> Vault -> Vault
|
||||||
addAccessToken token (Vault vault) =
|
addAccessToken token (Vault vault) =
|
||||||
Envelope.mapContext (\c -> { c | suggestedAccessToken = Just token }) vault
|
Envelope.mapContext (\c -> { c | suggestedAccessToken = Just token }) vault
|
||||||
|
@ -74,16 +77,39 @@ addAccessToken token (Vault vault) =
|
||||||
|
|
||||||
-}
|
-}
|
||||||
fromUserId : String -> Maybe Vault
|
fromUserId : String -> Maybe Vault
|
||||||
fromUserId =
|
fromUserId uid =
|
||||||
User.fromString
|
uid
|
||||||
>> Maybe.map
|
|> User.fromString
|
||||||
|
|> Maybe.map
|
||||||
(\u ->
|
(\u ->
|
||||||
Envelope.init
|
Envelope.init
|
||||||
{ serverName = "https://" ++ User.domain u
|
{ serverName = "https://" ++ User.domain u
|
||||||
, content = Internal.init u
|
, content = Internal.init (Just u)
|
||||||
}
|
}
|
||||||
|
|> Envelope.mapContext (\c -> { c | username = Just uid })
|
||||||
)
|
)
|
||||||
>> Maybe.map Vault
|
|> Maybe.map Vault
|
||||||
|
|
||||||
|
|
||||||
|
{-| Using a username and an address, create a Vault.
|
||||||
|
|
||||||
|
The username can either be the localpart or the full Matrix ID. For example,
|
||||||
|
you can either insert `alice` or `@alice:example.org`.
|
||||||
|
|
||||||
|
-}
|
||||||
|
fromUsername : { username : String, host : String, port_ : Maybe Int } -> Vault
|
||||||
|
fromUsername { username, host, port_ } =
|
||||||
|
{ serverName =
|
||||||
|
port_
|
||||||
|
|> Maybe.map String.fromInt
|
||||||
|
|> Maybe.map ((++) ":")
|
||||||
|
|> Maybe.withDefault ""
|
||||||
|
|> (++) host
|
||||||
|
, content = Internal.init (User.fromString username)
|
||||||
|
}
|
||||||
|
|> Envelope.init
|
||||||
|
|> Envelope.mapContext (\c -> { c | username = Just username })
|
||||||
|
|> Vault
|
||||||
|
|
||||||
|
|
||||||
{-| Send a message event to a room.
|
{-| Send a message event to a room.
|
||||||
|
@ -94,8 +120,18 @@ exists and the user is able to send a message to, and instead just sends the
|
||||||
request to the Matrix API.
|
request to the Matrix API.
|
||||||
|
|
||||||
-}
|
-}
|
||||||
sendMessageEvent : Vault -> { content : E.Value, eventType : String, roomId : String, toMsg : VaultUpdate -> msg, transactionId : String } -> Cmd msg
|
sendMessageEvent :
|
||||||
sendMessageEvent (Vault vault) data =
|
{ content : E.Value
|
||||||
|
, eventType : String
|
||||||
|
, roomId : String
|
||||||
|
, toMsg : VaultUpdate -> msg
|
||||||
|
, transactionId : String
|
||||||
|
, vault : Vault
|
||||||
|
}
|
||||||
|
-> Cmd msg
|
||||||
|
sendMessageEvent data =
|
||||||
|
case data.vault of
|
||||||
|
Vault vault ->
|
||||||
Api.sendMessageEvent vault
|
Api.sendMessageEvent vault
|
||||||
{ content = data.content
|
{ content = data.content
|
||||||
, eventType = data.eventType
|
, eventType = data.eventType
|
||||||
|
|
|
@ -2,6 +2,8 @@ module Matrix.Settings exposing
|
||||||
( setAccessToken, removeAccessToken
|
( setAccessToken, removeAccessToken
|
||||||
, getDeviceName, setDeviceName
|
, getDeviceName, setDeviceName
|
||||||
, getSyncTime, setSyncTime
|
, getSyncTime, setSyncTime
|
||||||
|
, setPassword
|
||||||
|
, removePassword, removePasswordOnLogin
|
||||||
)
|
)
|
||||||
|
|
||||||
{-| The Matrix Vault has lots of configurable variables that you rarely want to
|
{-| The Matrix Vault has lots of configurable variables that you rarely want to
|
||||||
|
@ -50,20 +52,39 @@ The value is in miliseconds, so it is set at 30,000.
|
||||||
|
|
||||||
@docs getSyncTime, setSyncTime
|
@docs getSyncTime, setSyncTime
|
||||||
|
|
||||||
|
|
||||||
|
## Password
|
||||||
|
|
||||||
|
When a Vault wants to access the Matrix API, it needs an access token. This can
|
||||||
|
either be provided directly, or the Vault can get one itself by using a password
|
||||||
|
to log in.
|
||||||
|
|
||||||
|
@docs setPassword
|
||||||
|
|
||||||
|
For security reasons, it is not possible to read whatever password is stored in
|
||||||
|
the Vault. An attacker with access to the memory might be able to find it,
|
||||||
|
however, so the Vault offers ways to remove the password from memory.
|
||||||
|
|
||||||
|
@docs removePassword, removePasswordOnLogin
|
||||||
|
|
||||||
-}
|
-}
|
||||||
|
|
||||||
import Internal.Values.Envelope as Envelope
|
import Internal.Values.Envelope as Envelope
|
||||||
import Types exposing (Vault(..))
|
import Types exposing (Vault(..))
|
||||||
|
|
||||||
|
|
||||||
{-| Insert a suggested access token.
|
{-| Determine the device name.
|
||||||
-}
|
-}
|
||||||
setAccessToken : String -> Vault -> Vault
|
getDeviceName : Vault -> String
|
||||||
setAccessToken token (Vault vault) =
|
getDeviceName (Vault vault) =
|
||||||
vault
|
Envelope.extractSettings .deviceName vault
|
||||||
|> Envelope.mapContext
|
|
||||||
(\c -> { c | suggestedAccessToken = Just token })
|
|
||||||
|> Vault
|
{-| Determine the sync timeout value.
|
||||||
|
-}
|
||||||
|
getSyncTime : Vault -> Int
|
||||||
|
getSyncTime (Vault vault) =
|
||||||
|
Envelope.extractSettings .syncTime vault
|
||||||
|
|
||||||
|
|
||||||
{-| Remove an access token that has been inserted using the
|
{-| Remove an access token that has been inserted using the
|
||||||
|
@ -80,11 +101,32 @@ removeAccessToken (Vault vault) =
|
||||||
|> Vault
|
|> Vault
|
||||||
|
|
||||||
|
|
||||||
{-| Determine the device name.
|
{-| Remove a password that is stored in the Matrix Vault.
|
||||||
-}
|
-}
|
||||||
getDeviceName : Vault -> String
|
removePassword : Vault -> Vault
|
||||||
getDeviceName (Vault vault) =
|
removePassword (Vault vault) =
|
||||||
Envelope.extractSettings .deviceName vault
|
vault
|
||||||
|
|> Envelope.mapContext
|
||||||
|
(\c -> { c | password = Nothing })
|
||||||
|
|> Vault
|
||||||
|
|
||||||
|
|
||||||
|
{-| Remove password from the Vault as soon as a valid access token has been
|
||||||
|
received from the Matrix API.
|
||||||
|
-}
|
||||||
|
removePasswordOnLogin : Bool -> Vault -> Vault
|
||||||
|
removePasswordOnLogin b (Vault vault) =
|
||||||
|
Vault <| Envelope.mapSettings (\s -> { s | removePasswordOnLogin = b }) vault
|
||||||
|
|
||||||
|
|
||||||
|
{-| Insert a suggested access token.
|
||||||
|
-}
|
||||||
|
setAccessToken : String -> Vault -> Vault
|
||||||
|
setAccessToken token (Vault vault) =
|
||||||
|
vault
|
||||||
|
|> Envelope.mapContext
|
||||||
|
(\c -> { c | suggestedAccessToken = Just token })
|
||||||
|
|> Vault
|
||||||
|
|
||||||
|
|
||||||
{-| Override the device name.
|
{-| Override the device name.
|
||||||
|
@ -94,11 +136,14 @@ setDeviceName name (Vault vault) =
|
||||||
Vault <| Envelope.mapSettings (\s -> { s | deviceName = name }) vault
|
Vault <| Envelope.mapSettings (\s -> { s | deviceName = name }) vault
|
||||||
|
|
||||||
|
|
||||||
{-| Determine the sync timeout value.
|
{-| Set a password for the given user.
|
||||||
-}
|
-}
|
||||||
getSyncTime : Vault -> Int
|
setPassword : String -> Vault -> Vault
|
||||||
getSyncTime (Vault vault) =
|
setPassword password (Vault vault) =
|
||||||
Envelope.extractSettings .syncTime vault
|
vault
|
||||||
|
|> Envelope.mapContext
|
||||||
|
(\c -> { c | password = Just password })
|
||||||
|
|> Vault
|
||||||
|
|
||||||
|
|
||||||
{-| Override the sync timeout value.
|
{-| Override the sync timeout value.
|
||||||
|
|
|
@ -19,4 +19,4 @@ vault =
|
||||||
|> Fuzz.map Dict.fromList
|
|> Fuzz.map Dict.fromList
|
||||||
)
|
)
|
||||||
(TestHashdict.fuzzer .roomId TestRoom.fuzzer)
|
(TestHashdict.fuzzer .roomId TestRoom.fuzzer)
|
||||||
TestUser.fuzzer
|
(Fuzz.maybe TestUser.fuzzer)
|
||||||
|
|
Loading…
Reference in New Issue