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:
parent
5b6c547b9a
commit
4fe28255fe
4
go.mod
4
go.mod
|
@ -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
14
go.sum
|
@ -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=
|
||||
|
|
33
main.go
33
main.go
|
@ -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,19 +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())
|
||||
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,
|
||||
}
|
||||
var connectionString string
|
||||
if strings.Contains(config.Database.ConnectionString, "?") {
|
||||
connectionString = config.Database.ConnectionString + "&search_path=\"" + message.ServiceID.String() + "\""
|
||||
} else {
|
||||
// Try to switch schemas
|
||||
_, err = pluginConn.Exec("SET search_path TO " + message.ServiceID.String())
|
||||
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{
|
||||
|
@ -180,8 +177,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.Postgres,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
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.Exec("CREATE TABLE IF NOT EXISTS global (key BLOB NOT NULL, uniquenessCheck BOOLEAN NOT NULL UNIQUE CHECK (uniquenessCheck = true) DEFAULT true)")
|
||||
_, 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.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)")
|
||||
_, 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.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\"]')")
|
||||
_, 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,10 +377,11 @@ func Main(information library.ServiceInitializationInformation) {
|
|||
})
|
||||
|
||||
router.Get("/authorize", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Query().Get("client_id") != "" {
|
||||
if conn.DBType == library.Sqlite {
|
||||
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)
|
||||
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)
|
||||
|
@ -349,7 +391,6 @@ func Main(information library.ServiceInitializationInformation) {
|
|||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !bytes.Equal(creator, serviceIDBytes) {
|
||||
renderTemplate(200, w, map[string]interface{}{
|
||||
|
@ -362,6 +403,35 @@ func Main(information library.ServiceInitializationInformation) {
|
|||
"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 {
|
||||
http.Redirect(w, r, "/dashboard", 301)
|
||||
}
|
||||
})
|
||||
|
||||
router.Get("/dashboard", func(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -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"`
|
||||
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{
|
||||
|
|
|
@ -107,10 +107,6 @@ 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(), "?"))
|
||||
|
@ -119,13 +115,6 @@ func main() {
|
|||
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
|
||||
}
|
||||
}
|
||||
|
||||
var statusBox = js.Global().Get("document").Call("getElementById", "statusBox")
|
||||
var autoAccept = js.Global().Get("document").Call("getElementById", "autoAccept")
|
||||
|
||||
|
|
|
@ -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(
|
||||
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,
|
||||
"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!")
|
||||
|
|
|
@ -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(
|
||||
func hashPassword(password string, salt []byte) []byte {
|
||||
return argon2.IDKey(
|
||||
[]byte(password),
|
||||
salt,
|
||||
32,
|
||||
19264,
|
||||
1,
|
||||
32,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -77,11 +76,13 @@ func main() {
|
|||
usernameBox.Set("disabled", true)
|
||||
passwordBox.Set("disabled", true)
|
||||
signupButton.Set("disabled", true)
|
||||
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,6 +205,7 @@ func main() {
|
|||
|
||||
captchaInProgress := false
|
||||
captchaButton.Call("addEventListener", "click", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
if localStorage.Call("getItem", "DEBUG-customCaptcha").IsNull() {
|
||||
if !captchaInProgress {
|
||||
captchaInProgress = true
|
||||
captchaButton.Set("innerText", "Pause")
|
||||
|
@ -254,6 +249,15 @@ func main() {
|
|||
} else {
|
||||
captchaInProgress = false
|
||||
}
|
||||
} else {
|
||||
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
|
||||
}))
|
||||
|
|
|
@ -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("a)
|
||||
userBytes, err := user.MarshalBinary()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
err = conn.DB.QueryRow("SELECT quota FROM quotas WHERE id = $1", userBytes).Scan("a)
|
||||
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,12 +401,19 @@ 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 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
|
||||
// Log the error message to the logger service
|
||||
|
|
Loading…
Reference in New Issue