Updated packages, removed unneeded double-check, switched over to RFC-2024-0008 as the authentication protocol, updated fulgens library, fixed broken quotas, fixed a broken JSON validator

Signed-off-by: Arzumify <jliwin98@danwin1210.de>
This commit is contained in:
Tracker-Friendly 2024-10-13 19:20:19 +01:00
parent 5b6c547b9a
commit 4fe28255fe
8 changed files with 349 additions and 243 deletions

4
go.mod
View File

@ -3,7 +3,7 @@ module git.ailur.dev/ailur/fulgens
go 1.23.1
require (
git.ailur.dev/ailur/fg-library v1.0.0
git.ailur.dev/ailur/fg-library v1.0.1
git.ailur.dev/ailur/fg-nucleus-library v1.0.0
git.ailur.dev/ailur/pow v1.0.0
github.com/cespare/xxhash/v2 v2.3.0
@ -27,7 +27,7 @@ require (
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/stretchr/testify v1.9.0 // indirect
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
golang.org/x/net v0.30.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.19.0 // indirect

14
go.sum
View File

@ -1,5 +1,5 @@
git.ailur.dev/ailur/fg-library v1.0.0 h1:61RkJW9g4PqAiZFjpwUnx6QUYYSeXJXLjLi1d47NfTA=
git.ailur.dev/ailur/fg-library v1.0.0/go.mod h1:hOUkxs2rRouSwNnNZlo7CsFVH12kmjqheyzPQ4to1N8=
git.ailur.dev/ailur/fg-library v1.0.1 h1:7TY2shmYNfKPzCTeYC80uj+sFZPbBWeOlqKT6ZsKFmc=
git.ailur.dev/ailur/fg-library v1.0.1/go.mod h1:hOUkxs2rRouSwNnNZlo7CsFVH12kmjqheyzPQ4to1N8=
git.ailur.dev/ailur/fg-nucleus-library v1.0.0 h1:TT1V4cfka+uUpvV1zU7bc4KXFkgnsI/sIvaZDDxXk+k=
git.ailur.dev/ailur/fg-nucleus-library v1.0.0/go.mod h1:m4gNSEypfgrUV8bXaR8NLB8zchUM59y0ellV1wp/C+I=
git.ailur.dev/ailur/pow v1.0.0 h1:eCJiZSbskcmzmwR4Nv4YrYpsZci5kfoGM9ihkXAHHoU=
@ -46,12 +46,10 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
@ -61,8 +59,8 @@ golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=

47
main.go
View File

@ -41,7 +41,7 @@ type Config struct {
Database struct {
DatabaseType string `json:"databaseType" validate:"required,oneof=sqlite postgres"`
ConnectionString string `json:"connectionString" validate:"required_if=DatabaseType postgres"`
DatabasePath string `json:"databasePath" validate:"required_if=DatabaseType sqlite,isDirectory"`
DatabasePath string `json:"databasePath" validate:"required_if=DatabaseType sqlite"`
} `json:"database" validate:"required"`
Services map[string]interface{} `json:"services"`
}
@ -122,7 +122,10 @@ func processInterServiceMessage(channel chan library.InterServiceMessage, config
ForServiceID: message.ServiceID,
MessageType: 2,
SentAt: time.Now(),
Message: pluginConn,
Message: library.Database{
DB: pluginConn,
DBType: library.Sqlite,
},
}
}
} else if config.Database.DatabaseType == "postgres" {
@ -139,7 +142,7 @@ func processInterServiceMessage(channel chan library.InterServiceMessage, config
}
} else {
// Try to create the schema
_, err = conn.Exec("CREATE SCHEMA IF NOT EXISTS " + message.ServiceID.String())
_, err = conn.Exec("CREATE SCHEMA IF NOT EXISTS \"" + message.ServiceID.String() + "\"")
if err != nil {
// Report an error
services[message.ServiceID].Inbox <- library.InterServiceMessage{
@ -151,7 +154,13 @@ func processInterServiceMessage(channel chan library.InterServiceMessage, config
}
} else {
// Create a new connection to the database
pluginConn, err := sql.Open("postgres", config.Database.ConnectionString+" dbname="+message.ServiceID.String())
var connectionString string
if strings.Contains(config.Database.ConnectionString, "?") {
connectionString = config.Database.ConnectionString + "&search_path=\"" + message.ServiceID.String() + "\""
} else {
connectionString = config.Database.ConnectionString + "?search_path=\"" + message.ServiceID.String() + "\""
}
pluginConn, err := sql.Open("postgres", connectionString)
if err != nil {
// Report an error
services[message.ServiceID].Inbox <- library.InterServiceMessage{
@ -162,26 +171,16 @@ func processInterServiceMessage(channel chan library.InterServiceMessage, config
Message: err,
}
} else {
// Try to switch schemas
_, err = pluginConn.Exec("SET search_path TO " + message.ServiceID.String())
if err != nil {
// Report an error
services[message.ServiceID].Inbox <- library.InterServiceMessage{
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
ForServiceID: message.ServiceID,
MessageType: 1,
SentAt: time.Now(),
Message: err,
}
} else {
// Report a successful activation
services[message.ServiceID].Inbox <- library.InterServiceMessage{
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
ForServiceID: message.ServiceID,
MessageType: 2,
SentAt: time.Now(),
Message: pluginConn,
}
// Report a successful activation
services[message.ServiceID].Inbox <- library.InterServiceMessage{
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000001"),
ForServiceID: message.ServiceID,
MessageType: 2,
SentAt: time.Now(),
Message: library.Database{
DB: pluginConn,
DBType: library.Postgres,
},
}
}
}

View File

@ -165,7 +165,7 @@ func verifyJwt(token string, publicKey ed25519.PublicKey, mem *sql.DB) ([]byte,
}
func Main(information library.ServiceInitializationInformation) {
var conn *sql.DB
var conn library.Database
var mem *sql.DB
var publicKey ed25519.PublicKey
var privateKey ed25519.PrivateKey
@ -201,40 +201,81 @@ func Main(information library.ServiceInitializationInformation) {
if response.MessageType == 2 {
// This is the connection information
// Set up the database connection
conn = response.Message.(*sql.DB)
// Create the global table
// Uniqueness check is a hack to ensure we only have one global row
_, err := conn.Exec("CREATE TABLE IF NOT EXISTS global (key BLOB NOT NULL, uniquenessCheck BOOLEAN NOT NULL UNIQUE CHECK (uniquenessCheck = true) DEFAULT true)")
if err != nil {
logFunc(err.Error(), 3, information)
}
// Create the users table
_, err = conn.Exec("CREATE TABLE IF NOT EXISTS users (id BLOB PRIMARY KEY NOT NULL UNIQUE, created INTEGER NOT NULL, username TEXT NOT NULL UNIQUE, password BLOB NOT NULL, salt BLOB NOT NULL)")
if err != nil {
logFunc(err.Error(), 3, information)
}
// Create the oauth table
_, err = conn.Exec("CREATE TABLE IF NOT EXISTS oauth (appId TEXT NOT NULL UNIQUE, secret TEXT, creator BLOB NOT NULL, redirectUri TEXT NOT NULL, name TEXT NOT NULL, keyShareUri TEXT NOT NULL DEFAULT '', scopes TEXT NOT NULL DEFAULT '[\"openid\"]')")
if err != nil {
logFunc(err.Error(), 3, information)
conn = response.Message.(library.Database)
if conn.DBType == library.Sqlite {
// Create the global table
// Uniqueness check is a hack to ensure we only have one global row
_, err := conn.DB.Exec("CREATE TABLE IF NOT EXISTS global (key BLOB NOT NULL, uniquenessCheck BOOLEAN NOT NULL UNIQUE CHECK (uniquenessCheck = true) DEFAULT true)")
if err != nil {
logFunc(err.Error(), 3, information)
}
// Create the users table
_, err = conn.DB.Exec("CREATE TABLE IF NOT EXISTS users (id BLOB PRIMARY KEY NOT NULL UNIQUE, created INTEGER NOT NULL, username TEXT NOT NULL UNIQUE, publicKey BLOB NOT NULL)")
if err != nil {
logFunc(err.Error(), 3, information)
}
// Create the oauth table
_, err = conn.DB.Exec("CREATE TABLE IF NOT EXISTS oauth (appId TEXT NOT NULL UNIQUE, secret TEXT, creator BLOB NOT NULL, redirectUri TEXT NOT NULL, name TEXT NOT NULL, keyShareUri TEXT NOT NULL DEFAULT '', scopes TEXT NOT NULL DEFAULT '[\"openid\"]')")
if err != nil {
logFunc(err.Error(), 3, information)
}
} else {
// Create the global table
// Uniqueness check is a hack to ensure we only have one global row
_, err := conn.DB.Exec("CREATE TABLE IF NOT EXISTS global (key BYTEA NOT NULL, uniquenessCheck BOOLEAN NOT NULL UNIQUE CHECK (uniquenessCheck = true) DEFAULT true)")
if err != nil {
logFunc(err.Error(), 3, information)
}
// Create the users table
_, err = conn.DB.Exec("CREATE TABLE IF NOT EXISTS users (id BYTEA PRIMARY KEY NOT NULL UNIQUE, created INTEGER NOT NULL, username TEXT NOT NULL UNIQUE, password BYTEA NOT NULL, salt BYTEA NOT NULL)")
if err != nil {
logFunc(err.Error(), 3, information)
}
// Create the oauth table
_, err = conn.DB.Exec("CREATE TABLE IF NOT EXISTS oauth (appId TEXT NOT NULL UNIQUE, secret TEXT, creator BYTEA NOT NULL, redirectUri TEXT NOT NULL, name TEXT NOT NULL, keyShareUri TEXT NOT NULL DEFAULT '', scopes TEXT NOT NULL DEFAULT '[\"openid\"]')")
if err != nil {
logFunc(err.Error(), 3, information)
}
}
// Set up the in-memory cache
mem, err = sql.Open("sqlite", "file:"+ServiceInformation.ServiceID.String()+"?mode=memory&cache=shared")
if err != nil {
logFunc(err.Error(), 3, information)
}
// Drop the tables if they exist
_, err = mem.Exec("DROP TABLE IF EXISTS sessions")
if err != nil {
logFunc(err.Error(), 3, information)
}
_, err = mem.Exec("DROP TABLE IF EXISTS logins")
if err != nil {
logFunc(err.Error(), 3, information)
}
_, err = mem.Exec("DROP TABLE IF EXISTS spent")
if err != nil {
logFunc(err.Error(), 3, information)
}
_, err = mem.Exec("DROP TABLE IF EXISTS challengeResponse")
if err != nil {
logFunc(err.Error(), 3, information)
}
// Create the sessions table
_, err = mem.Exec("CREATE TABLE IF NOT EXISTS sessions (id BLOB NOT NULL, session TEXT NOT NULL, device TEXT NOT NULL DEFAULT '?')")
_, err = mem.Exec("CREATE TABLE sessions (id BLOB NOT NULL, session TEXT NOT NULL, device TEXT NOT NULL DEFAULT '?')")
if err != nil {
logFunc(err.Error(), 3, information)
}
// Create the logins table
_, err = mem.Exec("CREATE TABLE IF NOT EXISTS logins (appId TEXT NOT NULL, exchangeCode TEXT NOT NULL UNIQUE, pkce TEXT, pkceMethod TEXT, openid BOOLEAN NOT NULL, userId BLOB NOT NULL UNIQUE, nonce TEXT NOT NULL DEFAULT '', token TEXT NOT NULL)")
_, err = mem.Exec("CREATE TABLE logins (appId TEXT NOT NULL, exchangeCode TEXT NOT NULL UNIQUE, pkce TEXT, pkceMethod TEXT, openid BOOLEAN NOT NULL, userId BLOB NOT NULL UNIQUE, nonce TEXT NOT NULL DEFAULT '', token TEXT NOT NULL)")
if err != nil {
logFunc(err.Error(), 3, information)
}
// Create the spent PoW table
_, err = mem.Exec("CREATE TABLE IF NOT EXISTS spent (hash BLOB NOT NULL UNIQUE, expires INTEGER NOT NULL)")
_, err = mem.Exec("CREATE TABLE spent (hash BLOB NOT NULL UNIQUE, expires INTEGER NOT NULL)")
if err != nil {
logFunc(err.Error(), 3, information)
}
// Create the challenge-response table
_, err = mem.Exec("CREATE TABLE challengeResponse (challenge TEXT NOT NULL UNIQUE, userId BLOB NOT NULL, expires INTEGER NOT NULL)")
if err != nil {
logFunc(err.Error(), 3, information)
}
@ -246,7 +287,7 @@ func Main(information library.ServiceInitializationInformation) {
// Set up the signing keys
// Check if the global table has the keys
err = conn.QueryRow("SELECT key FROM global LIMIT 1").Scan(&privateKey)
err = conn.DB.QueryRow("SELECT key FROM global LIMIT 1").Scan(&privateKey)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
// Generate a new key
@ -256,7 +297,7 @@ func Main(information library.ServiceInitializationInformation) {
logFunc(err.Error(), 3, information)
}
// Insert the key into the global table
_, err = conn.Exec("INSERT INTO global (key) VALUES (?)", privateKey)
_, err = conn.DB.Exec("INSERT INTO global (key) VALUES ($1)", privateKey)
if err != nil {
logFunc(err.Error(), 3, information)
}
@ -268,14 +309,14 @@ func Main(information library.ServiceInitializationInformation) {
}
// Set up the test app
_, err = conn.Exec("DELETE FROM oauth WHERE appId = 'TestApp-DoNotUse'")
_, err = conn.DB.Exec("DELETE FROM oauth WHERE appId = 'TestApp-DoNotUse'")
if err != nil {
testAppIsAvailable = false
logFunc(err.Error(), 2, information)
}
if testAppIsInternalApp {
_, err = conn.Exec("INSERT INTO oauth (appId, secret, creator, name, redirectUri, scopes, keyShareUri) VALUES ('TestApp-DoNotUse', 'none', ?, 'Test App', ?, '[\"openid\", \"clientKeyShare\"]', ?)", serviceIDBytes, ensureTrailingSlash(hostName)+"testApp", ensureTrailingSlash(hostName)+"keyExchangeTester")
_, err = conn.DB.Exec("INSERT INTO oauth (appId, secret, creator, name, redirectUri, scopes, keyShareUri) VALUES ('TestApp-DoNotUse', 'none', $1, 'Test App', $2, '[\"openid\", \"clientKeyShare\"]', $3)", serviceIDBytes, ensureTrailingSlash(hostName)+"testApp", ensureTrailingSlash(hostName)+"keyExchangeTester")
} else {
testAppCreator, err := uuid.New().MarshalBinary()
if err != nil {
@ -283,7 +324,7 @@ func Main(information library.ServiceInitializationInformation) {
logFunc(err.Error(), 2, information)
}
_, err = conn.Exec("INSERT INTO oauth (appId, secret, creator, name, redirectUri, scopes, keyShareUri) VALUES ('TestApp-DoNotUse', 'none', ?, 'Test App', ?, '[\"openid\", \"clientKeyShare\"]', ?)", testAppCreator, ensureTrailingSlash(hostName)+"testApp", ensureTrailingSlash(hostName)+"keyExchangeTester")
_, err = conn.DB.Exec("INSERT INTO oauth (appId, secret, creator, name, redirectUri, scopes, keyShareUri) VALUES ('TestApp-DoNotUse', 'none', $1, 'Test App', $2, '[\"openid\", \"clientKeyShare\"]', $3)", testAppCreator, ensureTrailingSlash(hostName)+"testApp", ensureTrailingSlash(hostName)+"keyExchangeTester")
}
if err != nil {
testAppIsAvailable = false
@ -336,31 +377,60 @@ func Main(information library.ServiceInitializationInformation) {
})
router.Get("/authorize", func(w http.ResponseWriter, r *http.Request) {
var name string
var creator []byte
if r.URL.Query().Get("client_id") != "" {
err := conn.QueryRow("SELECT name, creator FROM oauth WHERE appId = ?", r.URL.Query().Get("client_id")).Scan(&name, &creator)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
renderString(404, w, "App not found", information)
} else {
logFunc(err.Error(), 2, information)
renderString(500, w, "Sorry, something went wrong on our end. Error code: 03. Please report to the administrator.", information)
if conn.DBType == library.Sqlite {
var name string
var creator []byte
err := conn.DB.QueryRow("SELECT name, creator FROM oauth WHERE appId = $1", r.URL.Query().Get("client_id")).Scan(&name, &creator)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
renderString(404, w, "App not found", information)
} else {
logFunc(err.Error(), 2, information)
renderString(500, w, "Sorry, something went wrong on our end. Error code: 03. Please report to the administrator.", information)
}
return
}
return
}
}
if !bytes.Equal(creator, serviceIDBytes) {
renderTemplate(200, w, map[string]interface{}{
"identifier": identifier,
"name": name,
}, "authorize.html", information)
if !bytes.Equal(creator, serviceIDBytes) {
renderTemplate(200, w, map[string]interface{}{
"identifier": identifier,
"name": name,
}, "authorize.html", information)
} else {
renderTemplate(200, w, map[string]interface{}{
"identifier": identifier,
"name": name,
}, "autoAccept.html", information)
}
} else {
var name string
var creator uuid.UUID
err := conn.DB.QueryRow("SELECT name, creator FROM oauth WHERE appId = $1", r.URL.Query().Get("client_id")).Scan(&name, &creator)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
renderString(404, w, "App not found", information)
} else {
logFunc(err.Error(), 2, information)
renderString(500, w, "Sorry, something went wrong on our end. Error code: 03. Please report to the administrator.", information)
}
return
}
if creator != ServiceInformation.ServiceID {
renderTemplate(200, w, map[string]interface{}{
"identifier": identifier,
"name": name,
}, "authorize.html", information)
} else {
renderTemplate(200, w, map[string]interface{}{
"identifier": identifier,
"name": name,
}, "autoAccept.html", information)
}
}
} else {
renderTemplate(200, w, map[string]interface{}{
"identifier": identifier,
"name": name,
}, "autoAccept.html", information)
http.Redirect(w, r, "/dashboard", 301)
}
})
@ -386,7 +456,7 @@ func Main(information library.ServiceInitializationInformation) {
// Check if they have the clientKeyShare scope
var scopes string
err = conn.QueryRow("SELECT scopes FROM oauth WHERE appId = ?", claims["aud"]).Scan(&scopes)
err = conn.DB.QueryRow("SELECT scopes FROM oauth WHERE appId = $1", claims["aud"]).Scan(&scopes)
if err != nil {
renderString(500, w, "Sorry, something went wrong on our end. Error code: 20. Please report to the administrator.", information)
logFunc(err.Error(), 2, information)
@ -472,7 +542,7 @@ func Main(information library.ServiceInitializationInformation) {
hashedPassword := argon2.IDKey(newPassword, salt, 64, 4096, 1, 32)
// Update the password
_, err = conn.Exec("UPDATE users SET password = ?, salt = ? WHERE id = ?", hashedPassword, salt, userId)
_, err = conn.DB.Exec("UPDATE users SET password = $1, salt = $2 WHERE id = $3", hashedPassword, salt, userId)
if err != nil {
renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "05"}, information)
logFunc(err.Error(), 2, information)
@ -494,8 +564,7 @@ func Main(information library.ServiceInitializationInformation) {
router.Post("/api/signup", func(w http.ResponseWriter, r *http.Request) {
type signup struct {
Username string `json:"username"`
Password string `json:"password"`
Salt string `json:"salt"`
PublicKey string `json:"publicKey"`
ProofOfWork string `json:"proofOfWork"`
}
var data signup
@ -544,29 +613,22 @@ func Main(information library.ServiceInitializationInformation) {
}
}
// Decode the password
password, err := base64.StdEncoding.DecodeString(data.Password)
if err != nil {
renderJSON(400, w, map[string]interface{}{"error": "Invalid JSON"}, information)
return
}
// Decode the salt
salt, err := base64.StdEncoding.DecodeString(data.Salt)
// Decode the public key
publicKey, err := base64.StdEncoding.DecodeString(data.PublicKey)
if err != nil {
renderJSON(400, w, map[string]interface{}{"error": "Invalid JSON"}, information)
return
}
// Try to insert the user
userId := uuid.New()
userIdBytes, err := userId.MarshalBinary()
userID, err := uuid.New().MarshalBinary()
if err != nil {
renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "08"}, information)
logFunc(err.Error(), 2, information)
return
}
_, err = conn.Exec("INSERT INTO users (id, created, username, password, salt, created) VALUES (?, ?, ?, ?, ?, ?)", userIdBytes, time.Now().Unix(), data.Username, password, salt, time.Now().Unix())
_, err = conn.DB.Exec("INSERT INTO users (id, created, username, publicKey, created) VALUES ($1, $2, $3, $4, $5)", userID, time.Now().Unix(), data.Username, publicKey, time.Now().Unix())
if err != nil {
if strings.Contains(err.Error(), "UNIQUE constraint failed") {
renderJSON(409, w, map[string]interface{}{"error": "Username already taken"}, information)
@ -588,7 +650,7 @@ func Main(information library.ServiceInitializationInformation) {
}
// Insert the session
_, err = mem.Exec("INSERT INTO sessions (id, session, device) VALUES (?, ?, ?)", userIdBytes, session, r.Header.Get("User-Agent"))
_, err = mem.Exec("INSERT INTO sessions (id, session, device) VALUES (?, ?, ?)", userID, session, r.Header.Get("User-Agent"))
// Return success, as well as the session token
renderJSON(200, w, map[string]interface{}{"key": session}, information)
@ -606,12 +668,12 @@ func Main(information library.ServiceInitializationInformation) {
return
}
// Get the salt for the user
var salt []byte
err = conn.QueryRow("SELECT salt FROM users WHERE username = ?", data.Username).Scan(&salt)
// Get the id for the user
var userId []byte
err = conn.DB.QueryRow("SELECT id FROM users WHERE username = $1", data.Username).Scan(&userId)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
renderJSON(401, w, map[string]interface{}{"error": "Invalid username or password"}, information)
renderJSON(401, w, map[string]interface{}{"error": "Invalid username"}, information)
} else {
renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "12"}, information)
logFunc(err.Error(), 2, information)
@ -619,14 +681,30 @@ func Main(information library.ServiceInitializationInformation) {
return
}
// Return the salt
renderJSON(200, w, map[string]interface{}{"salt": base64.StdEncoding.EncodeToString(salt)}, information)
// Generate a new challenge
challenge, err := randomChars(512)
if err != nil {
renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "53"}, information)
logFunc(err.Error(), 2, information)
return
}
// Insert the challenge with one minute expiration
_, err = mem.Exec("INSERT INTO challengeResponse (challenge, userId, expires) VALUES (?, ?, ?)", challenge, userId, time.Now().Unix()+60)
if err != nil {
renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "53"}, information)
logFunc(err.Error(), 2, information)
return
}
// Return the challenge
renderJSON(200, w, map[string]interface{}{"challenge": challenge}, information)
})
router.Post("/api/login", func(w http.ResponseWriter, r *http.Request) {
type login struct {
Username string `json:"username"`
Password string `json:"password"`
Username string `json:"username"`
Signature string `json:"signature"`
}
var data login
@ -638,11 +716,11 @@ func Main(information library.ServiceInitializationInformation) {
// Try to select the user
var userId []byte
var hashedPassword []byte
err = conn.QueryRow("SELECT id, password FROM users WHERE username = ?", data.Username).Scan(&userId, &hashedPassword)
var publicKey []byte
err = conn.DB.QueryRow("SELECT id, publicKey FROM users WHERE username = $1", data.Username).Scan(&userId, &publicKey)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
renderJSON(401, w, map[string]interface{}{"error": "Invalid username or password"}, information)
renderJSON(401, w, map[string]interface{}{"error": "Invalid username"}, information)
} else {
renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "12"}, information)
logFunc(err.Error(), 2, information)
@ -650,16 +728,38 @@ func Main(information library.ServiceInitializationInformation) {
return
}
// Decode the password
password, err := base64.StdEncoding.DecodeString(data.Password)
// Decode the challenge
signature, err := base64.StdEncoding.DecodeString(data.Signature)
if err != nil {
renderJSON(400, w, map[string]interface{}{"error": "Invalid JSON"}, information)
return
}
// Verify the password
if !bytes.Equal(password, hashedPassword) {
renderJSON(401, w, map[string]interface{}{"error": "Invalid username or password"}, information)
// Verify the challenge
// Select the current challenge from the database
var challenge string
err = mem.QueryRow("SELECT challenge FROM challengeResponse WHERE userId = ?", userId).Scan(&challenge)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
renderJSON(401, w, map[string]interface{}{"error": "Invalid challenge"}, information)
} else {
renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "52"}, information)
logFunc(err.Error(), 2, information)
}
return
}
// Check if the challenge is correct by verifying the signature
if !ed25519.Verify(publicKey, []byte(challenge), signature) {
renderJSON(401, w, map[string]interface{}{"error": "Invalid signature"}, information)
return
}
// Delete the challenge
_, err = mem.Exec("DELETE FROM challengeResponse WHERE userId = ?", userId)
if err != nil {
renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "53"}, information)
logFunc(err.Error(), 2, information)
return
}
@ -708,7 +808,7 @@ func Main(information library.ServiceInitializationInformation) {
// Get the username and the creation date
var username string
var created int64
err = conn.QueryRow("SELECT username, created FROM users WHERE id = ?", userId).Scan(&username, &created)
err = conn.DB.QueryRow("SELECT username, created FROM users WHERE id = $1", userId).Scan(&username, &created)
if err != nil {
renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "15"}, information)
logFunc(err.Error(), 2, information)
@ -771,7 +871,7 @@ func Main(information library.ServiceInitializationInformation) {
// Check if they have the openid scope
var scopes string
err = conn.QueryRow("SELECT scopes FROM oauth WHERE appId = ?", claims["aud"]).Scan(&scopes)
err = conn.DB.QueryRow("SELECT scopes FROM oauth WHERE appId = $1", claims["aud"]).Scan(&scopes)
if err != nil {
renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "17"}, information)
logFunc(err.Error(), 2, information)
@ -802,7 +902,7 @@ func Main(information library.ServiceInitializationInformation) {
// Get the username
var username string
err := conn.QueryRow("SELECT username FROM users WHERE id = ?", userId).Scan(&username)
err := conn.DB.QueryRow("SELECT username FROM users WHERE id = $1", userId).Scan(&username)
if err != nil {
renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "19"}, information)
logFunc(err.Error(), 2, information)
@ -833,7 +933,7 @@ func Main(information library.ServiceInitializationInformation) {
// Verify the AppID, redirectUri and scopes
var appId, redirectUri, scopes string
err = conn.QueryRow("SELECT appId, redirectUri, scopes FROM oauth WHERE appId = ?", data.AppId).Scan(&appId, &redirectUri, &scopes)
err = conn.DB.QueryRow("SELECT appId, redirectUri, scopes FROM oauth WHERE appId = $1", data.AppId).Scan(&appId, &redirectUri, &scopes)
if err != nil {
renderJSON(404, w, map[string]interface{}{"error": "App not found"}, information)
return
@ -979,7 +1079,7 @@ func Main(information library.ServiceInitializationInformation) {
// Verify the secret
var secret string
err = conn.QueryRow("SELECT secret FROM oauth WHERE appId = ?", r.Form.Get("client_id")).Scan(&secret)
err = conn.DB.QueryRow("SELECT \"secret\" FROM oauth WHERE appId = $1", r.Form.Get("client_id")).Scan(&secret)
if err != nil {
renderJSON(404, w, map[string]interface{}{"error": "App not found"}, information)
return
@ -1013,7 +1113,7 @@ func Main(information library.ServiceInitializationInformation) {
var openIDTokenString string
if openid {
var username string
err := conn.QueryRow("SELECT username FROM users WHERE id = ?", userId).Scan(&username)
err := conn.DB.QueryRow("SELECT username FROM users WHERE id = $1", userId).Scan(&username)
if err != nil {
renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "29"}, information)
logFunc(err.Error(), 2, information)
@ -1100,7 +1200,7 @@ func Main(information library.ServiceInitializationInformation) {
}
// Delete the oauth entry
_, err = conn.Exec("DELETE FROM oauth WHERE appId = ? AND creator = ?", data.AppID, userId)
_, err = conn.DB.Exec("DELETE FROM oauth WHERE appId = $1 AND creator = $2", data.AppID, userId)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
renderJSON(404, w, map[string]interface{}{"error": "App not found"}, information)
@ -1176,9 +1276,9 @@ func Main(information library.ServiceInitializationInformation) {
// Insert the oauth entry
if clientKeyShare {
_, err = conn.Exec("INSERT INTO oauth (appId, secret, creator, name, redirectUri, scopes, keyShareUri) VALUES (?, ?, ?, ?, ?, ?, ?)", appId, secret, userId, data.Name, data.RedirectUri, scopes, data.KeyShareUri)
_, err = conn.DB.Exec("INSERT INTO oauth (appId, secret, creator, name, redirectUri, scopes, keyShareUri) VALUES ($1, $2, $3, $4, $5, $6, $7)", appId, secret, userId, data.Name, data.RedirectUri, scopes, data.KeyShareUri)
} else {
_, err = conn.Exec("INSERT INTO oauth (appId, secret, creator, name, redirectUri, scopes) VALUES (?, ?, ?, ?, ?, ?)", appId, secret, userId, data.Name, data.RedirectUri, scopes)
_, err = conn.DB.Exec("INSERT INTO oauth (appId, secret, creator, name, redirectUri, scopes) VALUES ($1, $2, $3, $4, $5, $6)", appId, secret, userId, data.Name, data.RedirectUri, scopes)
}
if err != nil {
renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "37"}, information)
@ -1211,7 +1311,7 @@ func Main(information library.ServiceInitializationInformation) {
}
// Get the apps
rows, err := conn.Query("SELECT appId, name, redirectUri, scopes, keyShareUri FROM oauth WHERE creator = ?", userId)
rows, err := conn.DB.Query("SELECT appId, name, redirectUri, scopes, keyShareUri FROM oauth WHERE creator = $1", userId)
if err != nil {
renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "38"}, information)
logFunc(err.Error(), 2, information)
@ -1286,7 +1386,7 @@ func Main(information library.ServiceInitializationInformation) {
}
// Delete the user
_, err = conn.Exec("DELETE FROM users WHERE id = ?", userId)
_, err = conn.DB.Exec("DELETE FROM users WHERE id = $1", userId)
if err != nil {
renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "42"}, information)
logFunc(err.Error(), 2, information)
@ -1302,7 +1402,7 @@ func Main(information library.ServiceInitializationInformation) {
}
// Delete the user's oauth entries
_, err = conn.Exec("DELETE FROM oauth WHERE creator = ?", userId)
_, err = conn.DB.Exec("DELETE FROM oauth WHERE creator = ?", userId)
if err != nil {
renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "44"}, information)
logFunc(err.Error(), 2, information)
@ -1405,7 +1505,7 @@ func Main(information library.ServiceInitializationInformation) {
}
// Delete the session
_, err = mem.Exec("DELETE FROM sessions WHERE id = ? AND session = ?", userId, data.Session)
_, err = mem.Exec("DELETE FROM sessions WHERE id = $1 AND session = ?", userId, data.Session)
if err != nil {
renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "49"}, information)
logFunc(err.Error(), 2, information)
@ -1438,7 +1538,7 @@ func Main(information library.ServiceInitializationInformation) {
}
// Get the users
rows, err := conn.Query("SELECT id, username, created FROM users")
rows, err := conn.DB.Query("SELECT id, username, created FROM users")
if err != nil {
renderJSON(500, w, map[string]interface{}{"error": "Internal server error", "code": "50"}, information)
logFunc(err.Error(), 2, information)
@ -1504,7 +1604,7 @@ func Main(information library.ServiceInitializationInformation) {
// Sleep for half an hour
time.Sleep(time.Minute * 30)
// Delete everything in the spent table past it's expiry date
// Delete everything in the spent and challenge-response tables that has expired
affected, err := mem.Exec("DELETE FROM spent WHERE expires < ?", time.Now().Unix())
if err != nil {
logFunc(err.Error(), 1, information)
@ -1513,7 +1613,17 @@ func Main(information library.ServiceInitializationInformation) {
if err != nil {
logFunc(err.Error(), 1, information)
} else {
logFunc("Spent cleanup complete, deleted "+strconv.FormatInt(affectedCount, 10)+" entries", 0, information)
affected, err := mem.Exec("DELETE FROM challengeResponse WHERE expires < ?", time.Now().Unix())
if err != nil {
logFunc(err.Error(), 1, information)
} else {
affectedCount2, err := affected.RowsAffected()
if err != nil {
logFunc(err.Error(), 1, information)
} else {
logFunc("Cleanup complete, deleted "+strconv.FormatInt(affectedCount+affectedCount2, 10)+" entries", 0, information)
}
}
}
}
}
@ -1614,9 +1724,9 @@ func Main(information library.ServiceInitializationInformation) {
// Insert the oauth entry
if clientKeyShare {
_, err = conn.Exec("INSERT INTO oauth (appId, secret, creator, name, redirectUri, scopes, keyShareUri) VALUES (?, ?, ?, ?, ?, ?, ?)", appId, secret, serviceIDBytes, message.Message.(authLibrary.OAuthInformation).Name, message.Message.(authLibrary.OAuthInformation).RedirectUri, scopes, message.Message.(authLibrary.OAuthInformation).KeyShareUri)
_, err = conn.DB.Exec("INSERT INTO oauth (appId, secret, creator, name, redirectUri, scopes, keyShareUri) VALUES ($1, $2, $3, $4, $5, $6, $7)", appId, secret, serviceIDBytes, message.Message.(authLibrary.OAuthInformation).Name, message.Message.(authLibrary.OAuthInformation).RedirectUri, scopes, message.Message.(authLibrary.OAuthInformation).KeyShareUri)
} else {
_, err = conn.Exec("INSERT INTO oauth (appId, secret, creator, name, redirectUri, scopes) VALUES (?, ?, ?, ?, ?, ?)", appId, secret, serviceIDBytes, message.Message.(authLibrary.OAuthInformation).Name, message.Message.(authLibrary.OAuthInformation).RedirectUri, scopes)
_, err = conn.DB.Exec("INSERT INTO oauth (appId, secret, creator, name, redirectUri, scopes) VALUES ($1, $2, $3, $4, $5, $6)", appId, secret, serviceIDBytes, message.Message.(authLibrary.OAuthInformation).Name, message.Message.(authLibrary.OAuthInformation).RedirectUri, scopes)
}
if err != nil {
information.Outbox <- library.InterServiceMessage{

View File

@ -107,23 +107,12 @@ func main() {
var query url.Values
// Redirect to dashboard if client_id is not a URL parameter
if js.Global().Get("window").Get("location").Get("search").String() == "" {
js.Global().Get("window").Get("location").Call("replace", "/dashboard")
} else {
// Parse the url parameters using url.ParseQuery
var err error
query, err = url.ParseQuery(strings.TrimPrefix(js.Global().Get("window").Get("location").Get("search").String(), "?"))
if err != nil {
js.Global().Get("document").Call("getElementById", "statusBox").Set("innerText", "Error parsing URL query: "+err.Error())
return
}
// Redirect to dashboard if client_id is not a URL parameter
if !query.Has("client_id") {
js.Global().Get("window").Get("location").Call("replace", "/dashboard")
return
}
// Parse the url parameters using url.ParseQuery
var err error
query, err = url.ParseQuery(strings.TrimPrefix(js.Global().Get("window").Get("location").Get("search").String(), "?"))
if err != nil {
js.Global().Get("document").Call("getElementById", "statusBox").Set("innerText", "Error parsing URL query: "+err.Error())
return
}
var statusBox = js.Global().Get("document").Call("getElementById", "statusBox")

View File

@ -2,6 +2,7 @@ package main
import (
"bytes"
"crypto/ed25519"
"encoding/base64"
"encoding/json"
"fmt"
@ -15,16 +16,14 @@ import (
var currentInputType = 0
func hashPassword(password string, salt []byte) string {
return base64.StdEncoding.EncodeToString(
argon2.IDKey(
[]byte(password),
salt,
32,
19264,
1,
32,
),
func hashPassword(password string, salt []byte) []byte {
return argon2.IDKey(
[]byte(password),
salt,
32,
19264,
1,
32,
)
}
@ -112,7 +111,7 @@ func main() {
statusBox.Set("innerText", "Hashing password...")
fmt.Println("Hashing password...")
// Fetch the salt from the server
// Fetch the challenge from the server
body, err := json.Marshal(map[string]interface{}{
"username": username,
})
@ -156,21 +155,17 @@ func main() {
}
if response.StatusCode == 200 {
// Decode the salt
salt, err := base64.StdEncoding.DecodeString(responseMap["salt"].(string))
if err != nil {
showInput(1, inputContainer, usernameBox, signupButton, passwordBox, backButton, inputNameBox, statusBox, nextButton)
statusBox.Set("innerText", "Error decoding salt: "+err.Error())
return
}
// Hash the password
hashedPassword := hashPassword(password, []byte(username))
hashedPassword := hashPassword(password, salt)
// Sign the challenge
signature := ed25519.Sign(ed25519.NewKeyFromSeed(hashedPassword), []byte(responseMap["challenge"].(string)))
// Hashed password computed, contact server
statusBox.Set("innerText", "Contacting server...")
signupBody := map[string]interface{}{
"username": username,
"password": hashedPassword,
"username": username,
"signature": base64.StdEncoding.EncodeToString(signature),
}
// Marshal the body
@ -181,7 +176,7 @@ func main() {
return
}
// Send the password to the server
// Send the request
requestUri, err = url.JoinPath(js.Global().Get("window").Get("location").Get("origin").String(), "/api/login")
if err != nil {
showInput(1, inputContainer, usernameBox, signupButton, passwordBox, backButton, inputNameBox, statusBox, nextButton)
@ -219,7 +214,7 @@ func main() {
fmt.Println("Logged in!")
statusBox.Set("innerText", "Setting up encryption keys...")
localStorage.Call("setItem", "DONOTSHARE-secretKey", responseMap["key"].(string))
localStorage.Call("setItem", "DONOTSHARE-clientKey", hashPassword(password, []byte("fg-auth-client")))
localStorage.Call("setItem", "DONOTSHARE-clientKey", base64.StdEncoding.EncodeToString(hashPassword(password, []byte("fg-auth-client"))))
// Redirect to app
statusBox.Set("innerText", "Welcome!")

View File

@ -2,6 +2,7 @@ package main
import (
"bytes"
"crypto/ed25519"
"crypto/rand"
"encoding/base64"
"encoding/binary"
@ -29,16 +30,14 @@ func showElements(show bool, elements ...js.Value) {
}
}
func hashPassword(password string, salt []byte) string {
return base64.StdEncoding.EncodeToString(
argon2.IDKey(
[]byte(password),
salt,
32,
19264,
1,
32,
),
func hashPassword(password string, salt []byte) []byte {
return argon2.IDKey(
[]byte(password),
salt,
32,
19264,
1,
32,
)
}
@ -77,10 +76,12 @@ func main() {
usernameBox.Set("disabled", true)
passwordBox.Set("disabled", true)
signupButton.Set("disabled", true)
if localStorage.Call("getItem", "CONFIG-captchaStarted").IsNull() {
captchaStatus.Set("innerText", "CAPTCHA not started - start CAPTCHA to signup.")
} else {
captchaStatus.Set("innerText", "Captcha calculation paused.")
if localStorage.Call("getItem", "DEBUG-customCaptcha").IsNull() {
if localStorage.Call("getItem", "CONFIG-captchaStarted").IsNull() {
captchaStatus.Set("innerText", "CAPTCHA not started - start CAPTCHA to signup.")
} else {
captchaStatus.Set("innerText", "Captcha calculation paused.")
}
}
var captcha string
@ -114,24 +115,17 @@ func main() {
// PoW challenge computed, hash password
statusBox.Set("innerText", "Hashing password...")
// Generate a random salt
salt := make([]byte, 32)
_, err := rand.Read(salt)
if err != nil {
showElements(true, inputContainer, signupButton, loginButton)
statusBox.Set("innerText", "Error generating salt: "+err.Error())
return
}
// Hash the password
hashedPassword := hashPassword(password, salt)
hashedPassword := hashPassword(password, []byte(username))
// Create a keypair from the password
publicKey := ed25519.NewKeyFromSeed(hashedPassword).Public().(ed25519.PublicKey)
// Hashed password computed, contact server
statusBox.Set("innerText", "Contacting server...")
signupBody := map[string]interface{}{
"username": username,
"password": hashedPassword,
"salt": base64.StdEncoding.EncodeToString(salt),
"publicKey": base64.StdEncoding.EncodeToString(publicKey),
"proofOfWork": captcha,
}
@ -180,7 +174,7 @@ func main() {
// Signup successful
statusBox.Set("innerText", "Setting up encryption keys...")
localStorage.Call("setItem", "DONOTSHARE-secretKey", responseMap["key"].(string))
localStorage.Call("setItem", "DONOTSHARE-clientKey", hashPassword(password, []byte("fg-auth-client")))
localStorage.Call("setItem", "DONOTSHARE-clientKey", base64.StdEncoding.EncodeToString(hashPassword(password, []byte("fg-auth-client"))))
// Redirect to app
statusBox.Set("innerText", "Welcome!")
@ -211,48 +205,58 @@ func main() {
captchaInProgress := false
captchaButton.Call("addEventListener", "click", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
if !captchaInProgress {
captchaInProgress = true
captchaButton.Set("innerText", "Pause")
localStorage.Call("setItem", "CONFIG-captchaStarted", "true")
go func() {
if localStorage.Call("getItem", "DEBUG-customCaptcha").IsNull() {
if !captchaInProgress {
captchaInProgress = true
captchaButton.Set("innerText", "Pause")
localStorage.Call("setItem", "CONFIG-captchaStarted", "true")
go func() {
time.Sleep(time.Minute * 5)
if captchaInProgress {
captchaStatus.Set("innerText", "Taking a long time? Try the desktop version.")
go func() {
time.Sleep(time.Minute * 5)
if captchaInProgress {
captchaStatus.Set("innerText", "Taking a long time? Try the desktop version.")
}
}()
for {
if !captchaInProgress {
captchaStatus.Set("innerText", "Captcha calculation paused.")
captchaButton.Set("innerText", "Start")
break
} else {
captchaStatus.Set("innerText", "Calculating captcha... Stopping or refreshing will not lose progress.")
powParams, powResult, err := pow("fg-auth-signup")
if err != nil {
captchaStatus.Set("innerText", "Error calculating captcha: "+err.Error())
captchaInProgress = false
break
}
if powResult[:2] == "00" {
localStorage.Call("removeItem", "CONFIG-captchaStarted")
captcha = "2:" + powParams
captchaStatus.Set("innerText", "Captcha calculated!")
captchaButton.Set("disabled", true)
captchaButton.Set("innerText", "Start")
usernameBox.Set("disabled", false)
passwordBox.Set("disabled", false)
signupButton.Set("disabled", false)
captchaInProgress = false
break
}
time.Sleep(time.Millisecond)
}
}
}()
for {
if !captchaInProgress {
captchaStatus.Set("innerText", "Captcha calculation paused.")
captchaButton.Set("innerText", "Start")
break
} else {
captchaStatus.Set("innerText", "Calculating captcha... Stopping or refreshing will not lose progress.")
powParams, powResult, err := pow("fg-auth-signup")
if err != nil {
captchaStatus.Set("innerText", "Error calculating captcha: "+err.Error())
captchaInProgress = false
break
}
if powResult[:2] == "00" {
localStorage.Call("removeItem", "CONFIG-captchaStarted")
captcha = "2:" + powParams
captchaStatus.Set("innerText", "Captcha calculated!")
captchaButton.Set("disabled", true)
captchaButton.Set("innerText", "Start")
usernameBox.Set("disabled", false)
passwordBox.Set("disabled", false)
signupButton.Set("disabled", false)
captchaInProgress = false
break
}
time.Sleep(time.Millisecond)
}
}
}()
} else {
captchaInProgress = false
}
} else {
captchaInProgress = false
captcha = localStorage.Call("getItem", "DEBUG-customCaptcha").String()
captchaStatus.Set("innerText", "Captcha calculated!")
captchaButton.Set("disabled", true)
captchaButton.Set("innerText", "Start")
usernameBox.Set("disabled", false)
passwordBox.Set("disabled", false)
signupButton.Set("disabled", false)
}
return nil

View File

@ -41,16 +41,20 @@ var ServiceInformation = library.Service{
ServiceID: uuid.MustParse("00000000-0000-0000-0000-000000000003"),
}
var conn *sql.DB
var conn library.Database
func getQuota(user uuid.UUID, information library.ServiceInitializationInformation) (int64, error) {
// Get the user's quota from the database
var quota int64
err := conn.QueryRow("SELECT quota FROM quotas WHERE id = $1", user).Scan(&quota)
userBytes, err := user.MarshalBinary()
if err != nil {
return 0, err
}
err = conn.DB.QueryRow("SELECT quota FROM quotas WHERE id = $1", userBytes).Scan(&quota)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
// The user has no quota set, so we'll set it to the default quota
_, err = conn.Exec("INSERT INTO quotas (id, quota) VALUES ($1, $2)", user, int64(information.Configuration["defaultQuota"].(float64)))
_, err = conn.DB.Exec("INSERT INTO quotas (id, quota) VALUES ($1, $2)", userBytes, int64(information.Configuration["defaultQuota"].(float64)))
if err != nil {
return 0, err
}
@ -397,11 +401,18 @@ func Main(information library.ServiceInitializationInformation) {
if response.MessageType == 2 {
// This is the connection information
// Set up the database connection
conn = response.Message.(*sql.DB)
conn = response.Message.(library.Database)
// Create the quotas table if it doesn't exist
_, err := conn.Exec("CREATE TABLE IF NOT EXISTS quotas (id UUID PRIMARY KEY, quota BIGINT)")
if err != nil {
logFunc(err.Error(), 3, information)
if conn.DBType == library.Sqlite {
_, err := conn.DB.Exec("CREATE TABLE IF NOT EXISTS quotas (id BLOB PRIMARY KEY, quota BIGINT)")
if err != nil {
logFunc(err.Error(), 3, information)
}
} else {
_, err := conn.DB.Exec("CREATE TABLE IF NOT EXISTS quotas (id BYTEA PRIMARY KEY, quota BIGINT)")
if err != nil {
logFunc(err.Error(), 3, information)
}
}
} else {
// This is an error message