93 lines
2.8 KiB
FSharp
93 lines
2.8 KiB
FSharp
module Bootstrap
|
|
|
|
open System
|
|
|
|
type App =
|
|
{ Name: string
|
|
User: string
|
|
Org: string
|
|
Scopes: string list
|
|
Client: string }
|
|
|
|
type Session = { User: string; State: obj }
|
|
|
|
type Store =
|
|
{ Read: string -> Map<string, string>
|
|
Write: string -> Map<string, string> -> unit }
|
|
|
|
type Quay =
|
|
{ LoginAuth: unit -> string
|
|
Login: string -> string -> string -> Session
|
|
Access: string -> App -> unit
|
|
Application: string -> App -> string
|
|
Assign: string -> Session -> App -> unit
|
|
Issue: Session -> App -> string }
|
|
|
|
type Config = { AdminPath: string }
|
|
|
|
let apps =
|
|
[ { Name = "candidates-reader"
|
|
User = "candidates-bot"
|
|
Org = "candidates"
|
|
Scopes = [ "repo:read" ]
|
|
Client = "" }
|
|
{ Name = "candidates-writer"
|
|
User = "candidates-bot"
|
|
Org = "candidates"
|
|
Scopes = [ "repo:create"; "repo:write" ]
|
|
Client = "" }
|
|
{ Name = "mirror-reader"
|
|
User = "mirror-bot"
|
|
Org = "mirror"
|
|
Scopes = [ "repo:read" ]
|
|
Client = "" }
|
|
{ Name = "mirror-writer"
|
|
User = "mirror-bot"
|
|
Org = "mirror"
|
|
Scopes = [ "repo:create"; "repo:write" ]
|
|
Client = "" } ]
|
|
|
|
let personaPath user = $"secret/data/quay/personas/{user}"
|
|
let tokenPath app = $"secret/data/quay/oauth/{app.Name}"
|
|
|
|
let private field name (values: Map<string, string>) =
|
|
match Map.tryFind name values with
|
|
| Some value when not (String.IsNullOrWhiteSpace value) && value <> "PLACEHOLDER" -> value
|
|
| _ -> invalidOp "credential is absent"
|
|
|
|
let run config store quay =
|
|
let pending =
|
|
apps
|
|
|> List.filter (fun app ->
|
|
let values = store.Read(tokenPath app)
|
|
|
|
match Map.tryFind "token" values with
|
|
| Some value when not (String.IsNullOrWhiteSpace value) && value <> "PLACEHOLDER" -> false
|
|
| _ -> true)
|
|
|
|
if not pending.IsEmpty then
|
|
let admin = store.Read config.AdminPath |> field "token"
|
|
|
|
let loginAuth = quay.LoginAuth()
|
|
|
|
let sessions =
|
|
pending
|
|
|> List.map _.User
|
|
|> List.distinct
|
|
|> List.map (fun user ->
|
|
let password = store.Read(personaPath user) |> field "password"
|
|
let session = quay.Login user password loginAuth
|
|
|
|
let app = pending |> List.find (fun app -> app.User = user)
|
|
|
|
quay.Access admin app
|
|
user, session)
|
|
|> Map.ofList
|
|
|
|
for definition in pending do
|
|
let client = quay.Application admin definition
|
|
let app = { definition with Client = client }
|
|
let session = sessions[app.User]
|
|
quay.Assign admin session app
|
|
let token = quay.Issue session app
|
|
store.Write (tokenPath app) (Map.ofList (List.singleton ("token", token)))
|