Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

auto jwt secret #36

Merged
merged 5 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ https://diary-log.fly.dev/ (try with a random email is ok)
## set env var

```sh
DATABASE_URL: db url
JWT_AUDIENCE: audience_xxx
JWT_SECRET: jwt_secret_xxx
DATABASE_URL: postgresql://etc
```

## check docker-compose.yaml

docker-compose up -d
Expand Down
72 changes: 72 additions & 0 deletions api/Note.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ open Microsoft.AspNetCore.Http
open Falco
open System.Security.Claims
open Npgsql
open System.Text.Json

let forbidden =
let message = "Access to the resource is forbidden."
Expand Down Expand Up @@ -175,3 +176,74 @@ let login: HttpHandler =
let jwt = createNewUser conn email password
Json.Response.ofJson jwt)
ctx

open System.Text.Json

// find all todo_list in note and return todo_list as json str
let extractTodoList (note: string) =
let jsonDocument = JsonDocument.Parse(note)
let root = jsonDocument.RootElement

let rec extractTodoItems (element: JsonElement) =
seq {
match element.ValueKind with
| JsonValueKind.Object ->
match element.TryGetProperty("type") with
| true, typeProperty when typeProperty.GetString() = "todo_list" ->
yield element
| _ ->
for property in element.EnumerateObject() do
yield! extractTodoItems property.Value
| JsonValueKind.Array ->
for item in element.EnumerateArray() do
yield! extractTodoItems item
| _ -> ()
}

extractTodoItems root |> Seq.toList


let getAllTodoLists conn userId =
let diaries = Diary.ListDiaryByUserID conn userId
diaries
|> List.map (fun diary ->
{| noteId = diary.NoteId; todoList = extractTodoList diary.Note |})


let todoListsHandler : HttpHandler =
fun ctx ->
let conn = ctx.getNpgsql ()
let userId = int (ctx.User.FindFirst("user_id").Value)
let todoLists = getAllTodoLists conn userId
// print todolists
printfn "%A" todoLists
// construct a tiptap doc with todoLists and note_id
let tiptapDoc =
{|
``type`` = "doc"
content = [|
for todoList in todoLists do
yield JsonSerializer.Deserialize<JsonElement>("""
{
"type": "heading",
"attrs": {
"textAlign": null,
"indent": null,
"lineHeight": null,
"level": 3
},
"content": [
{
"type": "text",
"text": """ + todoList.noteId + """
}
]
}
""")
yield! todoList.todoList
|]
|}

printfn "%A" tiptapDoc

Json.Response.ofJson tiptapDoc ctx
59 changes: 52 additions & 7 deletions api/Program.fs
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,56 @@ let authenticateRouteMiddleware (app: IApplicationBuilder) =

app.Use(middleware)



let getOrCreateJwtSecret pgConn jwtAudienceName =
let getExistingSecret () =
try
let secret = JwtSecrets.GetJwtSecret pgConn jwtAudienceName
printfn "Existing JWT Secret found for %s" jwtAudienceName
Some secret
with :? NoResultsException -> None

let generateRandomKey () =
System.Convert.ToBase64String(System.Security.Cryptography.RandomNumberGenerator.GetBytes(32))

let getJwtKey () =
match Util.getEnvVar "JWT_SECRET" with
| null ->
let randomKey = generateRandomKey()
printfn "Warning: JWT_SECRET not set. Using randomly generated key: %s" randomKey
randomKey
| key -> key

let getAudience () =
match Util.getEnvVar "JWT_AUDIENCE" with
| null ->
let defaultAudience = "http://localhost:5000"
printfn "Warning: JWT_AUDIENCE not set. Using default audience: %s" defaultAudience
defaultAudience
| aud -> aud

let createNewSecret () =
let jwtSecretParams: JwtSecrets.CreateJwtSecretParams =
{ Name = jwtAudienceName
Secret = getJwtKey()
Audience = getAudience() }
let createdSecret = JwtSecrets.CreateJwtSecret pgConn jwtSecretParams
printfn "New JWT Secret created for %s" jwtAudienceName
createdSecret

match getExistingSecret() with
| Some secret -> secret
| None -> createNewSecret()

let authService (services: IServiceCollection) =
let jwtKey = Util.getEnvVar "JWT_SECRET"
let audience = Util.getEnvVar "JWT_AUDIENCE"
let connectionString = Database.Config.connStr
use pgConn = new Npgsql.NpgsqlConnection(connectionString)
pgConn.Open()

let jwtAudienceName = "logbook"

let jwtSecret = getOrCreateJwtSecret pgConn jwtAudienceName

pgConn.Close()

let _ =
services
Expand All @@ -72,9 +115,9 @@ let authService (services: IServiceCollection) =
ValidateIssuer = false,
//ValidIssuer = Configuratio["Jwt:Issuer"],
ValidateAudience = true,
ValidAudience = audience,
ValidAudience = jwtSecret.Audience,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(jwtKey))
IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(jwtSecret.Secret))
))

services
Expand Down Expand Up @@ -102,7 +145,9 @@ webHost [||] {
[ post "/api/login" Note.login
get "/api/diary" Note.noteAllPart
get "/api/diary/{id}" Note.noteByIdPartDebug
put "/api/diary/{id}" Note.addNotePart ]
put "/api/diary/{id}" Note.addNotePart
get "/api/todo" Note.todoListsHandler
]

use_middleware serveVueFiles
}
5 changes: 1 addition & 4 deletions api/Util.fs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
module Util

let getEnvVar varName =
match System.Environment.GetEnvironmentVariable(varName) with
| null -> failwith (sprintf "%s environment variable not found" varName)
| value -> value
let getEnvVar varName = System.Environment.GetEnvironmentVariable(varName)
142 changes: 142 additions & 0 deletions api/tests/sample_diary_todo_list.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
[
{
"noteId": "20240630",
"todoList": [
{
"type": "todo_list",
"content": [
{
"type": "todo_item",
"attrs": {
"done": false,
"textAlign": null,
"lineHeight": null
},
"content": [
{
"type": "paragraph",
"attrs": {
"textAlign": null,
"indent": null,
"lineHeight": null
},
"content": [
{
"type": "text",
"text": "a"
}
]
}
]
},
{
"type": "todo_item",
"attrs": {
"done": false,
"textAlign": null,
"lineHeight": null
},
"content": [
{
"type": "paragraph",
"attrs": {
"textAlign": null,
"indent": null,
"lineHeight": null
},
"content": [
{
"type": "text",
"text": "b"
}
]
}
]
},
{
"type": "todo_item",
"attrs": {
"done": false,
"textAlign": null,
"lineHeight": null
},
"content": [
{
"type": "paragraph",
"attrs": {
"textAlign": null,
"indent": null,
"lineHeight": null
},
"content": [
{
"type": "text",
"text": "c"
}
]
}
]
}
]
}
]
},
{
"noteId": "20240830",
"todoList": [
{
"type": "todo_list",
"content": [
{
"type": "todo_item",
"attrs": {
"done": true,
"textAlign": null,
"lineHeight": null
},
"content": [
{
"type": "paragraph",
"attrs": {
"textAlign": null,
"indent": null,
"lineHeight": null
},
"content": [
{
"type": "text",
"text": "tedst"
}
]
}
]
},
{
"type": "todo_item",
"attrs": {
"done": false,
"textAlign": null,
"lineHeight": null
},
"content": [
{
"type": "paragraph",
"attrs": {
"textAlign": null,
"indent": null,
"lineHeight": null
},
"content": [
{
"type": "text",
"text": "test"
}
]
}
]
}
]
}
]
}
]
Loading
Loading