-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathoathkeeper.fsx
286 lines (241 loc) · 8.94 KB
/
oathkeeper.fsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
(*
module UserConfig =
let read
let save
module Ory =
let register
let login
If we call script w/o any argv ->
check if .user_config file is present in the current dir
| Yes -> login
| No ->
ask for login or register
| register -> register
| _ -> login
*)
open System
open System.Diagnostics
open System.IO
open System.Net
open System.Net.Http
open System.Net.Http.Headers
open System.Text
open System.Text.Json
type UserConfig =
{ Email: string
Password: string
KratosUrl: string }
module Runner =
let runUntilEnd (cmd: string) (args: string) =
use proc =
new Process(StartInfo = ProcessStartInfo(cmd, args, RedirectStandardOutput = true, UseShellExecute = false))
proc.Start() |> ignore
let sb = StringBuilder()
while proc.HasExited |> not do
sb.Append(proc.StandardOutput.ReadToEnd())
|> ignore
sb |> string
let runInBackground (cmd: string) (arg: string) =
let proc = Process.Start(cmd, arg)
proc.Id
module UserConfig =
let private secureAsk msg =
printf msg
let sb = StringBuilder()
let mutable key = ConsoleKeyInfo()
while key.Key <> ConsoleKey.Enter do
key <- Console.ReadKey true
if key.Key <> ConsoleKey.Enter then
sb.Append key.KeyChar |> ignore
printfn ""
sb |> string
let read path =
File.ReadAllText path
|> JsonSerializer.Deserialize<UserConfig>
let ask () =
printf "Please provide your username: "
let username = Console.ReadLine()
let password = secureAsk "Please provide your password: "
printf "Please provide Kratos URL [http://localhost:4433]: "
let kratosUrl = Console.ReadLine()
{ Email = username
Password = password
KratosUrl =
if kratosUrl.Length > 0 then
kratosUrl
else
"http://localhost:4433" }
let save path config =
let ctx = JsonSerializer.Serialize(config)
File.WriteAllText(path, ctx)
config
module Ory =
type SelfServiceResponse = { Id: string }
let private client = new HttpClient()
let private getToken (url: string) =
task {
let! res = client.GetAsync(url)
let! resStream = res.Content.ReadAsStreamAsync()
let! ssr =
JsonSerializer.DeserializeAsync<SelfServiceResponse>(
resStream,
JsonSerializerOptions(PropertyNameCaseInsensitive = true)
)
return
match ssr.Id with
| null -> None
| x -> Some x
}
let private getRegisterToken user =
getToken $"{user.KratosUrl}/self-service/registration/api"
let private getLoginToken user =
getToken $"{user.KratosUrl}/self-service/login/api"
let private registerUser id user =
task {
let url = $"{user.KratosUrl}/self-service/registration?flow={id}"
let body =
{| csrf_token = ""
method = "password"
password = user.Password
traits = {| email = user.Email |} |}
use stream = new MemoryStream()
do! JsonSerializer.SerializeAsync(stream, body)
stream.Position <- 0
use sr = new StreamReader(stream)
let! body = sr.ReadToEndAsync()
let body = new StringContent(body)
body.Headers.ContentType <- MediaTypeHeaderValue("application/json")
let! res = client.PostAsync(url, body)
let! resText = res.Content.ReadAsStringAsync()
return
if res.StatusCode = HttpStatusCode.OK then
Some res
else
printfn $"Registration error response:\n%O{resText}"
None
}
let private loginUser id user =
task {
let url = $"{user.KratosUrl}/self-service/login?flow={id}"
let body =
{| csrf_token = ""
method = "password"
password = user.Password
password_identifier = user.Email |}
use stream = new MemoryStream()
do! JsonSerializer.SerializeAsync(stream, body)
stream.Position <- 0
use sr = new StreamReader(stream)
let! body = sr.ReadToEndAsync()
let body = new StringContent(body)
body.Headers.ContentType <- MediaTypeHeaderValue("application/json")
let! res = client.PostAsync(url, body)
let! resText = res.Content.ReadAsStringAsync()
return
if res.StatusCode = HttpStatusCode.OK then
Some res
else
printfn $"Login error response:\n%O{resText}"
None
}
let register user =
try
match getRegisterToken user
|> Async.AwaitTask
|> Async.RunSynchronously
with
| None ->
printfn "Could not retrieve flowId"
Environment.Exit(1)
| Some x ->
match registerUser x user
|> Async.AwaitTask
|> Async.RunSynchronously
with
| None ->
printfn $"Could not register user: %s{user.Email}"
Environment.Exit(2)
| Some _ ->
printfn $"Successfully registered user: %s{user.Email}"
with
| :? AggregateException ->
printfn "Gitpod should have gone timeout. Please start it once again in Browser."
Environment.Exit(3)
let login user =
try
match getLoginToken user
|> Async.AwaitTask
|> Async.RunSynchronously
with
| None ->
printfn "Could not retrieve flowId"
Environment.Exit(1)
| Some x ->
match loginUser x user
|> Async.AwaitTask
|> Async.RunSynchronously
with
| None ->
printfn $"Could not log in user: %s{user.Email}"
Environment.Exit(2)
| Some _ ->
printfn $"Successfully logged in user: %s{user.Email}"
with
| :? AggregateException ->
printfn "Gitpod should have gone timeout. Please start it once again in Browser."
Environment.Exit(3)
let killBackgroundProcessOnCancel (pids: int []) =
Console.CancelKeyPress.AddHandler (fun _ ea ->
printfn $"Exiting script because %O{ea.SpecialKey} has been pressed"
for pid in pids do
try
let proc = Process.GetProcessById pid
printfn $"Killing process with PID: %d{proc.Id}"
proc.Kill()
with
| :? ArgumentException ->
printfn "Invalid PID provided"
())
let runOathkeeper (toolsDir: string) (configPath: string) =
let toolsDir =
if (toolsDir.EndsWith '/' || toolsDir.EndsWith '\\')
|> not then
toolsDir + Path.DirectorySeparatorChar.ToString()
else
toolsDir
let oathkeeperId = Runner.runInBackground $"%s{toolsDir}oathkeeper" $"serve -c %s{configPath}"
killBackgroundProcessOnCancel [| oathkeeperId |]
while true do
()
let main argv =
let defaultConfigPath = "./.user_config"
match List.ofArray argv with
| [ "register" ] ->
if File.Exists defaultConfigPath then
File.Delete defaultConfigPath
UserConfig.ask ()
|> UserConfig.save defaultConfigPath
|> Ory.register
| [] ->
if File.Exists defaultConfigPath then
Ory.login <| UserConfig.read defaultConfigPath
runOathkeeper "./bin/" "./.ory/config/oathkeeper/oathkeeper.yml"
else
printfn $"Please call `dotnet fsi {fsi.CommandLineArgs.[0]} register`"
Environment.Exit(1)
| [ x ] ->
if File.Exists defaultConfigPath then
Ory.login <| UserConfig.read defaultConfigPath
runOathkeeper x "./.ory/config/oathkeeper/oathkeeper.yml"
else
printfn $"Please call `dotnet fsi {fsi.CommandLineArgs.[0]} register`"
Environment.Exit(1)
| [x;c] ->
if File.Exists defaultConfigPath then
Ory.login <| UserConfig.read defaultConfigPath
runOathkeeper x c
else
printfn $"Please call `dotnet fsi {fsi.CommandLineArgs.[0]} register`"
Environment.Exit(1)
| _ -> failwith "Flow not supported"
main (fsi.CommandLineArgs |> Array.tail)