Separate password management into its own module
This commit is contained in:
parent
9db12cc731
commit
f4f619b354
3 changed files with 168 additions and 86 deletions
111
src/App.elm
111
src/App.elm
|
@ -5,6 +5,7 @@ import Gitlab
|
||||||
import Html
|
import Html
|
||||||
import Html.Events as Events
|
import Html.Events as Events
|
||||||
import Paginated
|
import Paginated
|
||||||
|
import Pass
|
||||||
import Ports
|
import Ports
|
||||||
import RemoteData
|
import RemoteData
|
||||||
import Task
|
import Task
|
||||||
|
@ -14,22 +15,16 @@ type alias ObjectsResponse =
|
||||||
RemoteData.WebData (List Gitlab.Object)
|
RemoteData.WebData (List Gitlab.Object)
|
||||||
|
|
||||||
|
|
||||||
type alias State =
|
|
||||||
{ client : Gitlab.Client
|
|
||||||
, repo : Gitlab.Repo
|
|
||||||
, objects : ObjectsResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
type Model
|
type Model
|
||||||
= Loading
|
= Loading
|
||||||
| Configuring Ports.Config
|
| Configuring Ports.Config
|
||||||
| Configured State
|
| Configured Pass.Model
|
||||||
|
|
||||||
|
|
||||||
type Msg
|
type Msg
|
||||||
= Loaded (Maybe Ports.Config)
|
= Loaded (Maybe Ports.Config)
|
||||||
| UpdateConfig Config.Msg
|
| UpdateConfig Config.Msg
|
||||||
|
| UpdatePass Pass.Msg
|
||||||
| SaveConfig
|
| SaveConfig
|
||||||
| Configure
|
| Configure
|
||||||
| GotObjects ObjectsResponse
|
| GotObjects ObjectsResponse
|
||||||
|
@ -57,15 +52,10 @@ subscriptions _ =
|
||||||
Ports.config Loaded
|
Ports.config Loaded
|
||||||
|
|
||||||
|
|
||||||
loadConfig : Ports.Config -> Maybe State
|
loadConfig : Ports.Config -> Maybe ( Pass.Model, Cmd Pass.Msg )
|
||||||
loadConfig cfg =
|
loadConfig cfg =
|
||||||
Maybe.map2
|
Maybe.map2
|
||||||
(\client repo ->
|
Pass.init
|
||||||
{ client = client
|
|
||||||
, repo = repo
|
|
||||||
, objects = RemoteData.NotAsked
|
|
||||||
}
|
|
||||||
)
|
|
||||||
(Config.toClient cfg)
|
(Config.toClient cfg)
|
||||||
(Config.toRepo cfg)
|
(Config.toRepo cfg)
|
||||||
|
|
||||||
|
@ -74,13 +64,16 @@ update : Msg -> Model -> ( Model, Cmd Msg )
|
||||||
update msg model =
|
update msg model =
|
||||||
case ( msg, model ) of
|
case ( msg, model ) of
|
||||||
( Loaded loaded, Loading ) ->
|
( Loaded loaded, Loading ) ->
|
||||||
( Debug.log "loaded" loaded
|
loaded
|
||||||
|> Maybe.andThen loadConfig
|
|> Maybe.andThen loadConfig
|
||||||
|> Maybe.map Configured
|
|> Maybe.map
|
||||||
|> Maybe.withDefault (Configuring Config.init)
|
(\( state, cmd ) ->
|
||||||
|> Debug.log "configured"
|
( Configured state, Cmd.map UpdatePass cmd )
|
||||||
, Cmd.none
|
)
|
||||||
)
|
|> Maybe.withDefault
|
||||||
|
( Configuring Config.init
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
( UpdateConfig update, Configuring cfg ) ->
|
( UpdateConfig update, Configuring cfg ) ->
|
||||||
( Configuring <|
|
( Configuring <|
|
||||||
|
@ -91,22 +84,16 @@ update msg model =
|
||||||
( SaveConfig, Configuring cfg ) ->
|
( SaveConfig, Configuring cfg ) ->
|
||||||
let
|
let
|
||||||
validated =
|
validated =
|
||||||
Maybe.map2
|
Maybe.map2 Pass.init
|
||||||
(\client repo ->
|
|
||||||
{ client = client
|
|
||||||
, repo = repo
|
|
||||||
, objects = RemoteData.NotAsked
|
|
||||||
}
|
|
||||||
)
|
|
||||||
(Config.toClient cfg)
|
(Config.toClient cfg)
|
||||||
(Config.toRepo cfg)
|
(Config.toRepo cfg)
|
||||||
in
|
in
|
||||||
case validated of
|
case validated of
|
||||||
Just state ->
|
Just ( state, cmd ) ->
|
||||||
( Configured state
|
( Configured state
|
||||||
, Cmd.batch
|
, Cmd.batch
|
||||||
[ Ports.saveConfig cfg
|
[ Ports.saveConfig cfg
|
||||||
, getObjects state.client state.repo
|
, Cmd.map UpdatePass cmd
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -118,10 +105,14 @@ update msg model =
|
||||||
, Cmd.none
|
, Cmd.none
|
||||||
)
|
)
|
||||||
|
|
||||||
( GotObjects objects, Configured state ) ->
|
( UpdatePass m, Configured state ) ->
|
||||||
( Configured { state | objects = objects }
|
let
|
||||||
, Cmd.none
|
( newstate, cmd ) =
|
||||||
)
|
Pass.update m state
|
||||||
|
in
|
||||||
|
( Configured newstate
|
||||||
|
, Cmd.map UpdatePass cmd
|
||||||
|
)
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
( model, Cmd.none )
|
( model, Cmd.none )
|
||||||
|
@ -141,53 +132,5 @@ view model =
|
||||||
]
|
]
|
||||||
|
|
||||||
Configured state ->
|
Configured state ->
|
||||||
Html.div []
|
Html.map UpdatePass <|
|
||||||
[ Html.button [ Events.onClick Configure ] [ Html.text "Configure" ]
|
Pass.view state
|
||||||
, Html.hr [] []
|
|
||||||
, viewFiles state.objects
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
viewFiles : ObjectsResponse -> Html.Html Msg
|
|
||||||
viewFiles data =
|
|
||||||
case data of
|
|
||||||
RemoteData.NotAsked ->
|
|
||||||
Html.div [] [ Html.text "Not loaded." ]
|
|
||||||
|
|
||||||
RemoteData.Failure _ ->
|
|
||||||
Html.div [] [ Html.text "Whoops." ]
|
|
||||||
|
|
||||||
RemoteData.Loading ->
|
|
||||||
Html.div [] [ Html.text "Loading objects..." ]
|
|
||||||
|
|
||||||
RemoteData.Success objects ->
|
|
||||||
let
|
|
||||||
files =
|
|
||||||
List.filter
|
|
||||||
(\o ->
|
|
||||||
o.objectType
|
|
||||||
== Gitlab.Blob
|
|
||||||
&& String.endsWith ".gpg" o.name
|
|
||||||
)
|
|
||||||
objects
|
|
||||||
in
|
|
||||||
Html.ul [] <|
|
|
||||||
List.map (\x -> Html.li [] [ viewFile x ]) files
|
|
||||||
|
|
||||||
|
|
||||||
viewFile : Gitlab.Object -> Html.Html Msg
|
|
||||||
viewFile file =
|
|
||||||
Html.text file.path
|
|
||||||
|
|
||||||
|
|
||||||
getConfig : Cmd Msg
|
|
||||||
getConfig =
|
|
||||||
Cmd.none
|
|
||||||
|
|
||||||
|
|
||||||
getObjects : Gitlab.Client -> Gitlab.Repo -> Cmd Msg
|
|
||||||
getObjects client repo =
|
|
||||||
Gitlab.getObjects repo client
|
|
||||||
|> Paginated.toTask
|
|
||||||
|> RemoteData.fromTask
|
|
||||||
|> Task.perform GotObjects
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import Json.Decode exposing (Decoder)
|
||||||
import Json.Decode.Pipeline exposing (decode, required)
|
import Json.Decode.Pipeline exposing (decode, required)
|
||||||
import Maybe.Extra
|
import Maybe.Extra
|
||||||
import Paginated
|
import Paginated
|
||||||
|
import RemoteData exposing (WebData)
|
||||||
import Task exposing (Task)
|
import Task exposing (Task)
|
||||||
|
|
||||||
|
|
||||||
|
@ -109,8 +110,12 @@ getObjects repo client =
|
||||||
client
|
client
|
||||||
|
|
||||||
|
|
||||||
getFiles : Repo -> Client -> Task Http.Error (List Object)
|
getFiles : Repo -> Client -> Task e (WebData (List String))
|
||||||
getFiles repo client =
|
getFiles repo client =
|
||||||
getObjects repo client
|
getObjects repo client
|
||||||
|> Paginated.toTask
|
|> Paginated.toTask
|
||||||
|> Task.map (List.filter (\o -> o.objectType == Blob))
|
|> Task.map
|
||||||
|
(List.filter (.objectType >> (==) Blob))
|
||||||
|
|> Task.map
|
||||||
|
(List.map .path)
|
||||||
|
|> RemoteData.fromTask
|
||||||
|
|
134
src/Pass.elm
Normal file
134
src/Pass.elm
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
module Pass exposing (..)
|
||||||
|
|
||||||
|
import Dict exposing (Dict)
|
||||||
|
import Gitlab
|
||||||
|
import Html exposing (Html)
|
||||||
|
import Html.Attributes as Attr
|
||||||
|
import Html.Events as Events
|
||||||
|
import Maybe.Extra
|
||||||
|
import Paginated
|
||||||
|
import Regex
|
||||||
|
import RemoteData exposing (WebData)
|
||||||
|
import Task
|
||||||
|
|
||||||
|
|
||||||
|
type alias Pass =
|
||||||
|
List String
|
||||||
|
|
||||||
|
|
||||||
|
type alias Details =
|
||||||
|
{ password : String
|
||||||
|
, metadata : Dict String String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type alias Credentials =
|
||||||
|
{ password : String
|
||||||
|
, username : Maybe String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type alias Model =
|
||||||
|
{ client : Gitlab.Client
|
||||||
|
, repo : Gitlab.Repo
|
||||||
|
, entries : WebData (List Pass)
|
||||||
|
, filter : String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type Msg
|
||||||
|
= GotEntries (WebData (List Pass))
|
||||||
|
| UpdateFilter String
|
||||||
|
| Decrypt Pass
|
||||||
|
| Decrypted String
|
||||||
|
|
||||||
|
|
||||||
|
init : Gitlab.Client -> Gitlab.Repo -> ( Model, Cmd Msg )
|
||||||
|
init client repo =
|
||||||
|
( { client = client
|
||||||
|
, repo = repo
|
||||||
|
, entries = RemoteData.NotAsked
|
||||||
|
, filter = ""
|
||||||
|
}
|
||||||
|
, getEntries client repo
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
fromFilename : String -> Maybe Pass
|
||||||
|
fromFilename filename =
|
||||||
|
if String.endsWith ".gpg" filename then
|
||||||
|
String.dropRight 4 filename
|
||||||
|
|> String.split "/"
|
||||||
|
|> Just
|
||||||
|
else
|
||||||
|
Nothing
|
||||||
|
|
||||||
|
|
||||||
|
toFilename : Pass -> String
|
||||||
|
toFilename pass =
|
||||||
|
(String.join "/" pass) ++ ".gpg"
|
||||||
|
|
||||||
|
|
||||||
|
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||||
|
update msg model =
|
||||||
|
case msg of
|
||||||
|
GotEntries entries ->
|
||||||
|
( { model | entries = entries }, Cmd.none )
|
||||||
|
|
||||||
|
UpdateFilter filter ->
|
||||||
|
( { model | filter = filter }, Cmd.none )
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
( model, Cmd.none )
|
||||||
|
|
||||||
|
|
||||||
|
view : Model -> Html Msg
|
||||||
|
view model =
|
||||||
|
Html.div []
|
||||||
|
[ Html.input
|
||||||
|
[ Attr.value model.filter
|
||||||
|
, Events.onInput UpdateFilter
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
, Html.hr [] []
|
||||||
|
, case model.entries of
|
||||||
|
RemoteData.NotAsked ->
|
||||||
|
Html.text "Loading"
|
||||||
|
|
||||||
|
RemoteData.Loading ->
|
||||||
|
Html.text "Loading..."
|
||||||
|
|
||||||
|
RemoteData.Failure _ ->
|
||||||
|
Html.text "Failed to load."
|
||||||
|
|
||||||
|
RemoteData.Success entries ->
|
||||||
|
Html.ul [] <|
|
||||||
|
List.map
|
||||||
|
(\x -> Html.li [] [ viewPass x ])
|
||||||
|
(filter model.filter entries)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
filter : String -> List Pass -> List Pass
|
||||||
|
filter search entries =
|
||||||
|
let
|
||||||
|
regex =
|
||||||
|
Regex.caseInsensitive (Regex.regex search)
|
||||||
|
|
||||||
|
matches : String -> Pass -> Bool
|
||||||
|
matches search entry =
|
||||||
|
List.any (Regex.contains regex) entry
|
||||||
|
in
|
||||||
|
List.filter (matches search) entries
|
||||||
|
|
||||||
|
|
||||||
|
viewPass : Pass -> Html Msg
|
||||||
|
viewPass pass =
|
||||||
|
Html.text (String.join "/" pass)
|
||||||
|
|
||||||
|
|
||||||
|
getEntries : Gitlab.Client -> Gitlab.Repo -> Cmd Msg
|
||||||
|
getEntries client repo =
|
||||||
|
Gitlab.getFiles repo client
|
||||||
|
|> Task.map (RemoteData.map (List.map fromFilename >> Maybe.Extra.values))
|
||||||
|
|> Task.perform GotEntries
|
Loading…
Reference in a new issue