From 3b70fe7b4b927c2de8d7eb6c890a1d4fe656c5f0 Mon Sep 17 00:00:00 2001 From: Arzumify Date: Sat, 20 Jul 2024 10:52:18 +0100 Subject: [PATCH] Removed a bunch of legacy APIs, added OAuth2 support, using Burgernotes 2.0 Draft 2 style encryption. --- APIDOCS.md | 81 +++++++--- main.go | 425 ++++++++++++++++++++++------------------------------- schema.sql | 12 +- 3 files changed, 247 insertions(+), 271 deletions(-) diff --git a/APIDOCS.md b/APIDOCS.md index 7519f28..6a00710 100644 --- a/APIDOCS.md +++ b/APIDOCS.md @@ -3,49 +3,86 @@ Use the Burgernotes API to automate tasks, build your own client, and more! Headers should be: "Content-type: application/json; charset=UTF-8" for all POSTs -## 🔑 Authentication (version 1) +## 🔑 Authentication POST - /api/signup - provide "username" and "password". -POST - /api/login - provide "username", "password" and "newpass" +POST - /api/login - provide "username" and "password" -To prevent the server from knowing the encryption key, the password you provide in the request must be hashed with the SHA-3 algorithm with 128 iterations (the hash is hashed again 128 times). +To prevent the server from knowing the encryption key, the password you provide in the request must be hashed with Argon2ID as per the following parameters: + +- salt: UTF-8 Representation of "I munch Burgers!!" (should be 16 bytes), +- parallelism: 1 +- iterations: 32 +- memorySize: 19264 bytes +- hashLength: 32 +- outputType: hexadecimal Password should be at least 8 characters, username must be under 20 characters and alphanumeric. -Newpass is a more direct call to /api/changepassword that is deprecated in version 2. Set newpass to a value of "no" in order to identify as a version 1 api and not trigger the backwards compatibility layer. - If username is taken, error code 422 will return. Assuming everything went correctly, the server will return a secret key. You'll need to store two things in local storage: - The secret key you just got, used to fetch notes, save stuff etc. -- A SHA512 hashed password, used as encryption key +- Another password, which is hashed the same way, but with a salt of "I love Burgernotes!" (again, UTF-8 representation, 16 bytes). -### Additional notes on version 2 +If you are using the OAuth2 flow (totally optional, I know it's really complex) then you should also store the login password to use later, or put the OAuth2 logic straight after this. -For version two, /api/signup and /api/login require the legacyPassword API, to allow for backwards compatibility up to version 0. To do this, set the header "X-Burgernotes-Version" to the current version number without any dots (E.G 2.1.4 -> 214). +## 🌐 OAuth2 + Burgerauth -During signup, "legacyPassword" should also be provided. legacyPassword should be the SHA-3 128 iteration hash of the Argon2ID hash of the password following these settings (yes, hashing a hash): +For security purposes, traditional OAuth2 is not supported. Instead, we use Burgerauth, a custom OAuth2 implementation that provides a unique-yet-consistent "authentication code" for each user. It is created in a special way as to not involve the server, meaning the security of Burgernotes is not compromised by using OAuth2, which is normally a very server-side process. +### How it works +First, perform regular client-side OAuth2 authentication using Burgerauth, as detailed in its [own documentation](https://concord.hectabit.org/HectaBit/burgerauth/src/branch/main/APIDOCS.md). Once regular OAuth2 is complete, you will be given an authentication code, which is important for the next step. + +You now have one of two options: +1. If your app is based on the web, you can host a static page provided [here](https://concord.hectabit.org/HectaBit/burgerauth/src/branch/main/keyExchangeRdir.html) on any static service. Redirect to this page with the OAuth2 token stored in localStorage as BURGERAUTH-RDIR-TOKEN. The page will then communicate with a corresponding page on Burgerauth, and transmit the key securely via RSA. You may see the page redirect a couple of times as it communicates the infomation across. All you need to know is that once it is finished, it will redirect back to the page that redirected to it with the key in localStorage as DONOTSHARE-EXCHANGED-KEY. +2. If your app is not web-based, you can open up a webview to [here](https://auth.hectabit.org/keyexchangeclient). Once it is finished, it will send a postMessage with the body "finished" to the target "*" and output "finished" to the JavaScript console. The key will be in localStorage as DONOTSHARE-EXCHANGED-KEY. +3. Alternatively, you can host a local webserver and host the aforementioned page on it. It will work the same way as the first option, and once it is finished, it will detect that it was not redirected to and instead will set it as a cookie expiring in 5 minutes and then refresh the page. You should detect for the cookie and use its value, and then kill the webserver. This method is not recommended because of its complexity and overhead. + +Once this is finished, you should check if there is an existing OAuth2 entry on the server like this: + +POST - /api/oauth/get - provide "username" and "oauthProvider" +oauthProvider is the name of the OAuth2 provider, such as "burgerauth" or "google" (google is used as an example, they do not use the burgerauth extensions and are therefore incompatible). +It does not have to be the actual name, but it has to be unique to the provider (per user). The sub given by OpenID Connect is a good choice. + +### 404 is returned +No entry has been found, and you have to log in the user as normal. +Once this is done, you should create an entry like this: + +POST - /api/oauth/new - provide "secretKey", "oauthProvider" and "encryptedPassword" + +To create encryptedPassword, follow these steps: + +1. Generate a random 16-byte IV. +2. Create a JSON structure like this: +```json +{ + "loginPass": "(the SHA-3 password hash created in the login process)", + "cryptoPass": "(the SHA-512 password hash stored in localStorage)" +} ``` -Parallelism should be 1 - -Iterations should be 256 - -Memory Allocated in bytes should be 512 - -Length of Hash should be 32 bytes - -The output should be in the encoded format, not the hashed format - -Salt should be the SHA512 of the password +3. Convert the JSON to a string and then encrypt it using AES-256 GCM using the exchangeKey as the key and the IV created earlier as the IV. +4. Create a JSON structure like this: +```json +{ + "iv": "(the IV)", + "content": "(the encrypted JSON)" +} ``` +5. Finally, convert the JSON into a string, base64 encode it, and send it off as encryptedPassword. -On login, as well as the key, the server may return "legacyPasswordNeeded" = true. +Do not store the exchangeKey after this point, as it is no longer needed. -If this is the case, POST /api/v2/addlegacypassword (with the aforementioned header), provide "secretKey" and "legacyPassword" (hashed the same way as signup). +### 200 is returned +An entry exists, and encryptedPassword has been returned using JSON. +encryptedPassword is the password encrypted using AES-256 GCM, and the IV is included in the JSON, in this format defined above. + +Use the passwords defined in the JSON structure before the last one to log in normally. + +#### Finally, you are done! ## 🔐 Encryption diff --git a/main.go b/main.go index eaf6a3c..7a88ae3 100644 --- a/main.go +++ b/main.go @@ -17,10 +17,9 @@ import ( "github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions/cookie" "github.com/gin-gonic/gin" - _ "github.com/mattn/go-sqlite3" + "github.com/mattn/go-sqlite3" "github.com/spf13/viper" "golang.org/x/crypto/scrypt" - "golang.org/x/crypto/sha3" ) var ( @@ -32,16 +31,15 @@ var ( saltChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" ) -func genSalt(length int) (string, error) { +func randomChars(length int) (string, error) { if length <= 0 { - log.Println("[ERROR] Known in genSalt() at", strconv.FormatInt(time.Now().Unix(), 10)+":", "Salt length must be at least one.") + return "", errors.New("salt length must be at least one") } salt := make([]byte, length) randomBytes := make([]byte, length) _, err := rand.Read(randomBytes) if err != nil { - log.Println("[ERROR] Unknown in genSalt() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) return "", err } @@ -51,24 +49,12 @@ func genSalt(length int) (string, error) { return string(salt), nil } -func hashSha3(iterations int, input string) string { - key := input - for i := 0; i < iterations; i++ { - hash := sha3.New512() - hash.Write([]byte(key)) - keyBytes := hash.Sum(nil) - key = hex.EncodeToString(keyBytes) - } - return key -} - func hash(password, salt string) (string, error) { passwordBytes := []byte(password) saltBytes := []byte(salt) derivedKey, err := scrypt.Key(passwordBytes, saltBytes, 32768, 8, 1, 64) if err != nil { - log.Println("[ERROR] Unknown in hash() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) return "", err } @@ -182,19 +168,19 @@ func initDb() { if os.IsNotExist(err) { err = generateDB() if err != nil { - log.Fatalln("[FATAL] Unknown while generating database at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Fatalln("[FATAL] Unknown while generating database:", err) } } else { log.Print("[PROMPT] Proceeding will overwrite the database. Proceed? (y/n): ") var answer string _, err := fmt.Scanln(&answer) if err != nil { - log.Fatalln("[FATAL] Unknown while scanning input at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Fatalln("[FATAL] Unknown while scanning input:", err) } if strings.ToLower(answer) == "y" { err := generateDB() if err != nil { - log.Fatalln("[FATAL] Unknown while generating database at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Fatalln("[FATAL] Unknown while generating database:", err) return } } else if answer == ":3" { @@ -210,21 +196,23 @@ func migrateDb() { if os.IsNotExist(err) { err = generateDB() if err != nil { - log.Fatalln("[FATAL] Unknown while generating database at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Fatalln("[FATAL] Unknown while generating database:", err) } } else { log.Println("[PROMPT] Proceeding will render the database unusable for older versions of Burgernotes. Proceed? (y/n): ") var answer string _, err := fmt.Scanln(&answer) if err != nil { - log.Fatalln("[FATAL] Unknown while scanning input at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Fatalln("[FATAL] Unknown while scanning input:", err) } if strings.ToLower(answer) == "y" { - _, err := conn.Exec("ALTER TABLE users ADD COLUMN versionTwoLegacyPassword TEXT NOT NULL DEFAULT 'nil'") + _, err = conn.Exec("ALTER TABLE users DROP COLUMN versionTwoLegacyPassword") if err != nil { - log.Fatalln("[FATAL] Unknown while migrating database at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[WARN] Unknown while migrating database (1/2):", err) + log.Println("[INFO] This is likely because your database is already migrated. This is not a problem, and Burgernotes does not need this removed - it is just for cleanup") return } + _, err = conn.Exec("CREATE TABLE oauth (id INTEGER NOT NULL, oauthProvider TEXT NOT NULL, encryptedPasswd TEXT NOT NULL)") } else if answer == ":3" { log.Println("[:3] :3") } else { @@ -248,7 +236,7 @@ func main() { err := viper.ReadInConfig() if err != nil { - log.Fatalln("[FATAL] Error in config file at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Fatalln("[FATAL] Error in config file:", err) } host = viper.GetString("config.HOST") @@ -276,12 +264,12 @@ func main() { conn, err = sql.Open("sqlite3", "database.db") if err != nil { - log.Fatalln("[FATAL] Cannot open database at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Fatalln("[FATAL] Cannot open database:", err) } defer func(conn *sql.DB) { err := conn.Close() if err != nil { - log.Println("[ERROR] Unknown in main() defer at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in main() defer:", err) } }(conn) @@ -314,12 +302,12 @@ func main() { }) router.GET("/api/version", func(c *gin.Context) { - c.String(200, "Burgernotes Version 2.0 Beta 1") + c.String(200, "Burgernotes Version 2.0 Beta 2") }) router.GET("/api/versionjson", func(c *gin.Context) { - c.JSON(200, gin.H{"name": "Burgernotes", "versiontxt": "Version 2.0 Beta 1", "versionsem": "2.0.0b1", "versionnum": "200"}) - }) + c.JSON(200, gin.H{"name": "Burgernotes", "versiontxt": "Version 2.0 Beta 2", "versionsem": "2.0.0b2", "versionnum": "200"}) + }) router.POST("/api/signup", func(c *gin.Context) { var data map[string]interface{} @@ -340,20 +328,6 @@ func main() { return } - enableAPIVersion2 := false - versionCheck := c.GetHeader("X-Burgernotes-Version") - if versionCheck != "" { - versionCheckInt, err := strconv.Atoi(versionCheck) - if err != nil { - log.Println("[ERROR] Unknown in /api/signup versionCheck at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) - c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SIGNUP-VERSIONCHECK"}) - return - } - if versionCheckInt > 199 { - enableAPIVersion2 = true - } - } - if username == "" || password == "" || len(username) > 20 || !regexp.MustCompile("^[a-zA-Z0-9]+$").MatchString(username) { c.JSON(422, gin.H{"error": "Invalid username or password"}) return @@ -361,7 +335,7 @@ func main() { _, taken, err := checkUsernameTaken(username) if err != nil { - log.Println("[ERROR] Unknown in /api/signup checkUsernameTaken() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/signup checkUsernameTaken():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SIGNUP-USERTAKEN"}) return } @@ -370,44 +344,22 @@ func main() { return } - salt, err := genSalt(16) + salt, err := randomChars(16) if err != nil { - log.Println("[ERROR] Unknown in /api/signup genSalt() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/signup randomChars():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SIGNUP-SALT"}) return } hashedPasswd, err := hash(password, salt) if err != nil { - log.Println("[ERROR] Unknown in /api/signup hash() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/signup hash():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SIGNUP-HASH"}) return } - if !enableAPIVersion2 { - _, err = conn.Exec("INSERT INTO users (username, password, versionTwoLegacyPassword, created) VALUES (?, ?, ?, ?)", username, hashedPasswd, "nil", strconv.FormatInt(time.Now().Unix(), 10)) - } else { - legacyPassword, ok := data["legacyPassword"].(string) - if !ok { - c.JSON(400, gin.H{"error": "Invalid JSON"}) - return - } - salt, err := genSalt(16) - if err != nil { - log.Println("[ERROR] Unknown in /api/signup genSalt() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) - c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SIGNUP-SALT"}) - return - } - legacyPasswordHash, err := hash(legacyPassword, salt) - if err != nil { - log.Println("[ERROR] Unknown in /api/signup hash() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) - c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SIGNUP-HASH"}) - return - } - _, err = conn.Exec("INSERT INTO users (username, password, versionTwoLegacyPassword, created) VALUES (?, ?, ?, ?)", username, hashedPasswd, legacyPasswordHash, strconv.FormatInt(time.Now().Unix(), 10)) - } - + _, err = conn.Exec("INSERT INTO users (username, password, created) VALUES (?, ?, ?)", username, hashedPasswd, strconv.FormatInt(time.Now().Unix(), 10)) if err != nil { - log.Println("[ERROR] Unknown in /api/signup Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/signup Exec():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SIGNUP-DBINSERT"}) return } @@ -421,15 +373,15 @@ func main() { return } - token, err := genSalt(512) + token, err := randomChars(512) if err != nil { - log.Println("[ERROR] Unknown in /api/signup token genSalt() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/signup token randomChars():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SIGNUP-SESSIONSALT"}) return } _, err = conn.Exec("INSERT INTO sessions (session, id, device) VALUES (?, ?, ?)", token, userid, c.Request.Header.Get("User-Agent")) if err != nil { - log.Println("[ERROR] Unknown in /api/signup session Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/signup session Exec():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SIGNUP-SESSIONINSERT"}) return } @@ -445,28 +397,6 @@ func main() { return } - enableAPIVersion2 := false - enableAPIVersion1 := false - version1PasswordChange, _ := data["newpass"].(string) - versionCheck := c.GetHeader("X-Burgernotes-Version") - if versionCheck != "" { - versionCheckInt, err := strconv.Atoi(versionCheck) - if err != nil { - log.Println("[ERROR] Unknown in /api/login versionCheck at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) - c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-VERSIONCHECK"}) - return - } - if versionCheckInt > 199 { - enableAPIVersion2 = true - } - } else { - if version1PasswordChange != "" { - enableAPIVersion1 = true - } else { - enableAPIVersion1 = false - } - } - username, ok := data["username"].(string) if !ok { c.JSON(400, gin.H{"error": "Invalid JSON"}) @@ -483,117 +413,52 @@ func main() { c.JSON(401, gin.H{"error": "User does not exist"}) return } else if err != nil { - log.Println("[ERROR] Unknown in /api/login checkUsernameTaken() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/login checkUsernameTaken():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-USERTAKEN"}) return } - if enableAPIVersion2 || enableAPIVersion1 { - _, _, hashedPasswd, err := getUser(userid) - if err != nil { - log.Println("[ERROR] Unknown in /api/login getUser() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) - c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-GETUSER"}) - return - } - - correctPassword, err := verifyHash(hashedPasswd, password) - if err != nil { - if errors.Is(err, errors.New("invalid hash format")) { - c.JSON(422, gin.H{"error": "Invalid hash format"}) - return - } else { - log.Println("[ERROR] Unknown in /api/login verifyHash() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) - c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-VERIFYHASH"}) - return - } - } - if !correctPassword { - c.JSON(401, gin.H{"error": "Incorrect password"}) - return - } - } else { - var legacyPassword string - err = conn.QueryRow("SELECT versionTwoLegacyPassword FROM users WHERE id = ?", userid).Scan(&legacyPassword) - if err != nil { - log.Println("[ERROR] Unknown in /api/login legacyPassword query at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) - c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-LEGACYQUERY"}) - return - } - hashedPassword := hashSha3(128, password) - - correctPassword, err := verifyHash(hashedPassword, password) - if err != nil { - if errors.Is(err, errors.New("invalid hash format")) { - c.JSON(422, gin.H{"error": "Invalid hash format"}) - return - } else { - log.Println("[ERROR] Unknown in /api/login verifyHash() legacy at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) - c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-VERIFYHASH"}) - return - } - } - - if !correctPassword { - c.JSON(401, gin.H{"error": "Incorrect password"}) - return - } - } - - if enableAPIVersion1 && version1PasswordChange != "null" { - salt, err := genSalt(16) - if err != nil { - log.Println("[ERROR] Unknown in /api/login genSalt() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) - c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-SALT"}) - return - } - hashedPassword, err := hash(version1PasswordChange, salt) - if err != nil { - log.Println("[ERROR] Unknown in /api/login hash() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) - c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-HASH"}) - return - } - _, err = conn.Exec("UPDATE users SET password = ? WHERE id = ?", hashedPassword, userid) - if err != nil { - log.Println("[ERROR] Unknown in /api/login Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) - c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-DBUPDATE"}) - return - } - } - - token, err := genSalt(512) + _, _, hashedPasswd, err := getUser(userid) if err != nil { - log.Println("[ERROR] Unknown in /api/login token genSalt() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/login getUser():", err) + c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-GETUSER"}) + return + } + + correctPassword, err := verifyHash(hashedPasswd, password) + if err != nil { + if errors.Is(err, errors.New("invalid hash format")) { + c.JSON(422, gin.H{"error": "Invalid hash format"}) + return + } else { + log.Println("[ERROR] Unknown in /api/login verifyHash():", err) + c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-VERIFYHASH"}) + return + } + } + if !correctPassword { + c.JSON(401, gin.H{"error": "Incorrect password"}) + return + } + + token, err := randomChars(512) + if err != nil { + log.Println("[ERROR] Unknown in /api/login token randomChars():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-SESSIONSALT"}) return } _, err = conn.Exec("INSERT INTO sessions (session, id, device) VALUES (?, ?, ?)", token, userid, c.Request.Header.Get("User-Agent")) if err != nil { - log.Println("[ERROR] Unknown in /api/login session Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/login session Exec():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-SESSIONINSERT"}) return } - if enableAPIVersion2 { - var legacyPassword string - err = conn.QueryRow("SELECT versionTwoLegacyPassword FROM users WHERE id = ?", userid).Scan(&legacyPassword) - if err != nil { - log.Println("[ERROR] Unknown in /api/login legacyPassword query at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) - c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-LEGACYQUERY"}) - return - } - if legacyPassword != "nil" { - c.JSON(200, gin.H{"key": token, "legacyPasswordNeeded": true}) - } else { - c.JSON(200, gin.H{"key": token, "legacyPasswordNeeded": false}) - } - return - } else { - c.JSON(200, gin.H{"key": token}) - } + c.JSON(200, gin.H{"key": token}) }) - router.POST("/api/v2/addlegacypassword", func(c *gin.Context) { + router.POST("/api/oauth/get", func(c *gin.Context) { var data map[string]interface{} err := c.ShouldBindJSON(&data) if err != nil { @@ -601,20 +466,42 @@ func main() { return } - versionCheck := c.GetHeader("X-Burgernotes-Version") - if versionCheck != "" { - versionCheckInt, err := strconv.Atoi(versionCheck) - if err != nil { - log.Println("[ERROR] Unknown in /api/login versionCheck at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) - c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-ADDLEGACYPASSWORD-VERSIONCHECK"}) - return + username, ok := data["username"].(string) + if !ok { + c.JSON(400, gin.H{"error": "Invalid JSON"}) + return + } + oauthProvider, ok := data["oauthProvider"].(string) + if !ok { + c.JSON(400, gin.H{"error": "Invalid JSON"}) + return + } + + _, userid, err := checkUsernameTaken(username) + if err != nil { + c.JSON(404, gin.H{"error": "Username not found"}) + return + } + + var encryptedPasswd string + err = conn.QueryRow("SELECT encryptedPasswd FROM oauth WHERE id = ? AND oauthProvider = ?", userid, oauthProvider).Scan(&encryptedPasswd) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + c.JSON(404, gin.H{"error": "Entry not found"}) + } else { + log.Println("[ERROR] Unknown in /api/oauth/get select:", err) + c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-OAUTH-GET-SELECT"}) } - if versionCheckInt < 200 { - c.JSON(400, gin.H{"error": "This API can only be accessed by Burgernotes 2.0 and above"}) - return - } - } else { - c.JSON(400, gin.H{"error": "This API can only be accessed by Burgernotes 2.0 and above"}) + } + + c.JSON(200, gin.H{"password": encryptedPasswd}) + }) + + router.POST("/api/oauth/add", func(c *gin.Context) { + var data map[string]interface{} + err := c.ShouldBindJSON(&data) + if err != nil { + c.JSON(400, gin.H{"error": "Invalid JSON"}) return } @@ -623,24 +510,70 @@ func main() { c.JSON(400, gin.H{"error": "Invalid JSON"}) return } - legacyPassword, ok := data["legacyPassword"].(string) + oauthProvider, ok := data["oauthProvider"].(string) if !ok { c.JSON(400, gin.H{"error": "Invalid JSON"}) return } + encryptedPassword, ok := data["encryptedPassword"].(string) + if !ok { + c.JSON(400, gin.H{"error": "Invalid JSON"}) + } + _, userid, err := getSession(token) if err != nil { c.JSON(401, gin.H{"error": "Invalid session"}) return } - _, err = conn.Exec("UPDATE users SET versionTwoLegacyPassword = ? WHERE id = ?", legacyPassword, userid) + _, err = conn.Exec("INSERT INTO oauth (id, oauthProvider, encryptedPasswd) VALUES (?, ?, ?)", userid, oauthProvider, encryptedPassword) if err != nil { - log.Println("[ERROR] Unknown in /api/login/addlegacypassword Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) - c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LOGIN-ADDLEGACYPASSWORD"}) + if errors.Is(err, sqlite3.ErrConstraintUnique) { + c.JSON(409, gin.H{"error": "Entry already exists"}) + } else { + log.Println("[ERROR] Unknown in /api/oauth/add Exec():", err) + c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-OAUTH-ADD-EXEC"}) + } + } + + c.JSON(200, gin.H{"success": true}) + }) + + router.POST("/api/oauth/remove", func(c *gin.Context) { + var data map[string]interface{} + err := c.ShouldBindJSON(&data) + if err != nil { + c.JSON(400, gin.H{"error": "Invalid JSON"}) return } + token, ok := data["secretKey"].(string) + if !ok { + c.JSON(400, gin.H{"error": "Invalid JSON"}) + return + } + oauthProvider, ok := data["oauthProvider"].(string) + if !ok { + c.JSON(400, gin.H{"error": "Invalid JSON"}) + return + } + + _, userid, err := getSession(token) + if err != nil { + c.JSON(401, gin.H{"error": "Invalid session"}) + return + } + + _, err = conn.Exec("DELETE FROM oauth WHERE userid = ? AND oauthProvider = ?", userid, oauthProvider) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + c.JSON(404, gin.H{"error": "Entry not found"}) + } else { + log.Println("[ERROR] Unknown in /api/oauth/add Exec():", err) + c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-OAUTH-REMOVE-EXEC"}) + } + } + c.JSON(200, gin.H{"success": true}) }) @@ -669,22 +602,22 @@ func main() { return } - salt, err := genSalt(16) + salt, err := randomChars(16) if err != nil { - log.Println("[ERROR] Unknown in /api/changepassword genSalt() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/changepassword randomChars():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-CHANGEPASSWORD-SALT"}) return } hashedPassword, err := hash(newPassword, salt) if err != nil { - log.Println("[ERROR] Unknown in /api/changepassword hash() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/changepassword hash():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-CHANGEPASSWORD-HASH"}) return } _, err = conn.Exec("UPDATE users SET password = ? WHERE id = ?", hashedPassword, userid) if err != nil { - log.Println("[ERROR] Unknown in /api/changepassword Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/changepassword Exec():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-CHANGEPASSWORD-DBUPDATE"}) return } @@ -713,21 +646,21 @@ func main() { created, username, _, err := getUser(userid) if err != nil { - log.Println("[ERROR] Unknown in /api/userinfo getUser() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/userinfo getUser():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-USERINFO-GETUSER"}) return } space, err := getSpace(userid) if err != nil { - log.Println("[ERROR] Unknown in /api/userinfo getSpace() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/userinfo getSpace():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-USERINFO-GETSPACE"}) return } notecount, err := getNoteCount(userid) if err != nil { - log.Println("[ERROR] Unknown in /api/userinfo getNoteCount() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/userinfo getNoteCount():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-USERINFO-GETNOTECOUNT"}) return } @@ -784,7 +717,7 @@ func main() { c.JSON(200, []map[string]interface{}{}) return } else { - log.Println("[ERROR] Unknown in /api/listnotes query at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/listnotes query:", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LISTNOTES-DBQUERY"}) return } @@ -792,7 +725,7 @@ func main() { defer func(rows *sql.Rows) { err := rows.Close() if err != nil { - log.Println("[ERROR] Unknown in /api/listnotes row defer at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/listnotes row defer:", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LISTNOTES-ROWCLOSE"}) return } @@ -803,14 +736,14 @@ func main() { var id int var title string if err := rows.Scan(&id, &title); err != nil { - log.Println("[ERROR] Unknown in /api/listnotes row scan at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/listnotes row scan:", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LISTNOTES-ROWSCAN"}) return } notes = append(notes, map[string]interface{}{"id": id, "title": title}) } if err := rows.Err(); err != nil { - log.Println("[ERROR] Unknown in /api/listnotes row iteration at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/listnotes row iteration:", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LISTNOTES-ROWERR"}) return } @@ -843,7 +776,7 @@ func main() { c.JSON(200, []map[string]interface{}{}) return } else { - log.Println("[ERROR] Unknown in /api/exportnotes query at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/exportnotes query:", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-EXPORTNOTES-DBQUERY"}) return } @@ -851,7 +784,7 @@ func main() { defer func(rows *sql.Rows) { err := rows.Close() if err != nil { - log.Println("[ERROR] Unknown in /api/exportnotes row defer at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/exportnotes row defer:", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-EXPORTNOTES-ROWCLOSE"}) return } @@ -862,14 +795,14 @@ func main() { var id int var created, edited, title, content string if err := rows.Scan(&id, &created, &edited, &title, &content); err != nil { - log.Println("[ERROR] Unknown in /api/exportnotes row scan at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/exportnotes row scan:", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-EXPORTNOTES-ROWSCAN"}) return } notes = append(notes, map[string]interface{}{"id": id, "created": created, "edited": edited, "title": title, "content": content}) } if err := rows.Err(); err != nil { - log.Println("[ERROR] Unknown in /api/exportnotes row iteration at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/exportnotes row iteration:", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-EXPORTNOTES-ROWERR"}) return } @@ -913,7 +846,7 @@ func main() { note := note.(map[string]interface{}) _, err := conn.Exec("INSERT INTO notes (creator, created, edited, title, content) VALUES (?, ?, ?, ?, ?)", userid, note["created"], note["edited"], note["title"], note["content"]) if err != nil { - log.Println("[ERROR] Unknown in /api/importnotes Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/importnotes Exec():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-IMPORTNOTES-DBINSERT"}) return } @@ -953,7 +886,7 @@ func main() { } else { _, err := conn.Exec("INSERT INTO notes (title, content, creator, created, edited) VALUES (?, ?, ?, ?, ?)", noteName, "", userid, strconv.FormatInt(time.Now().Unix(), 10), strconv.FormatInt(time.Now().Unix(), 10)) if err != nil { - log.Println("[ERROR] Unknown in /api/newnote Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/newnote Exec():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-NEWNOTE-DBINSERT"}) return } else { @@ -994,7 +927,7 @@ func main() { c.JSON(422, gin.H{"error": "Note not found"}) return } else { - log.Println("[ERROR] Unknown in /api/readnote getNote() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/readnote getNote():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-READNOTE-GETNOTE"}) return } @@ -1030,7 +963,7 @@ func main() { _, err = conn.Exec("DELETE FROM notes WHERE creator = ?", userid) if err != nil { - log.Println("[ERROR] Unknown in /api/purgenotes Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/purgenotes Exec():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-PURGENOTES-DBDELETE"}) return } else { @@ -1080,7 +1013,7 @@ func main() { c.JSON(422, gin.H{"error": "Note not found"}) return } else { - log.Println("[ERROR] Unknown in /api/editnote getNote() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/editnote getNote():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-EDITNOTE-GETNOTE"}) return } @@ -1092,7 +1025,7 @@ func main() { } else { _, err := conn.Exec("UPDATE notes SET content = ?, title = ?, edited = ? WHERE id = ?", content, title, strconv.FormatInt(time.Now().Unix(), 10), noteId) if err != nil { - log.Println("[ERROR] Unknown in /api/editnote Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/editnote Exec():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-EDITNOTE-DBUPDATE"}) return } else { @@ -1133,7 +1066,7 @@ func main() { c.JSON(422, gin.H{"error": "Note not found"}) return } else { - log.Println("[ERROR] Unknown in /api/removenote getNote() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/removenote getNote():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-REMOVENOTE-GETNOTE"}) return } @@ -1145,7 +1078,7 @@ func main() { } else { _, err := conn.Exec("DELETE FROM notes WHERE id = ?", noteId) if err != nil { - log.Println("[ERROR] Unknown in /api/removenote Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/removenote Exec():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-REMOVENOTE-DBDELETE"}) return } else { @@ -1176,21 +1109,21 @@ func main() { _, err = conn.Exec("DELETE FROM notes WHERE creator = ?", userid) if err != nil { - log.Println("[ERROR] Unknown in /api/deleteaccount notes Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/deleteaccount notes Exec():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-DELETEACCOUNT-NOTESDELETE"}) return } _, err = conn.Exec("DELETE FROM users WHERE id = ?", userid) if err != nil { - log.Println("[ERROR] Unknown in /api/deleteaccount user Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/deleteaccount user Exec():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-DELETEACCOUNT-USERDELETE"}) return } _, err = conn.Exec("DELETE FROM sessions WHERE id = ?", userid) if err != nil { - log.Println("[ERROR] Unknown in /api/deleteaccount session Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/deleteaccount session Exec():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-DELETEACCOUNT-SESSIONDELETE"}) return } @@ -1224,7 +1157,7 @@ func main() { c.JSON(200, []map[string]interface{}{}) return } else { - log.Println("[ERROR] Unknown in /api/sessions/list query at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/sessions/list query:", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SESSIONS-LIST-DBQUERY"}) return } @@ -1232,7 +1165,7 @@ func main() { defer func(rows *sql.Rows) { err := rows.Close() if err != nil { - log.Println("[ERROR] Unknown in /api/sessions/list row defer at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/sessions/list row defer:", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SESSIONS-LIST-ROWCLOSE"}) return } @@ -1243,7 +1176,7 @@ func main() { var sessionid int var session, device string if err := rows.Scan(&sessionid, &session, &device); err != nil { - log.Println("[ERROR] Unknown in /api/sessions/list row scan at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/sessions/list row scan:", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SESSIONS-LIST-ROWSCAN"}) return } @@ -1254,7 +1187,7 @@ func main() { } } if err := rows.Err(); err != nil { - log.Println("[ERROR] Unknown in /api/sessions/list row iteration at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/sessions/list row iteration:", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SESSIONS-LIST-ROWERR"}) return } @@ -1294,7 +1227,7 @@ func main() { c.JSON(422, gin.H{"error": "Target session not found"}) return } else { - log.Println("[ERROR] Unknown in /api/sessions/remove getSession() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/sessions/remove getSession():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SESSIONS-REMOVE-GETSESSION"}) return } @@ -1305,7 +1238,7 @@ func main() { } else { _, err := conn.Exec("DELETE FROM sessions WHERE sessionid = ?", sessionId) if err != nil { - log.Println("[ERROR] Unknown in /api/sessions/remove Exec() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/sessions/remove Exec():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-SESSIONS-REMOVE-DBDELETE"}) return } else { @@ -1335,7 +1268,7 @@ func main() { c.JSON(200, []map[string]interface{}{}) return } else { - log.Println("[ERROR] Unknown in /api/listusers query at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/listusers query:", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LISTUSERS-DBQUERY"}) return } @@ -1343,7 +1276,7 @@ func main() { defer func(rows *sql.Rows) { err := rows.Close() if err != nil { - log.Println("[ERROR] Unknown in /api/listusers row defer at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/listusers row defer:", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LISTUSERS-ROWCLOSE"}) return } @@ -1354,26 +1287,26 @@ func main() { var id int var username, created string if err := rows.Scan(&id, &username, &created); err != nil { - log.Println("[ERROR] Unknown in /api/listusers row scan at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/listusers row scan:", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LISTUSERS-ROWSCAN"}) return } space, err := getSpace(id) if err != nil { - log.Println("[ERROR] Unknown in /api/listusers getSpace() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/listusers getSpace():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LISTUSERS-GETSPACE"}) return } notes, err := getNoteCount(id) if err != nil { - log.Println("[ERROR] Unknown in /api/listusers getNoteCount() at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/listusers getNoteCount():", err) c.JSON(500, gin.H{"error": "Something went wrong on our end. Please report this bug at https://centrifuge.hectabit.org/hectabit/burgernotes and refer to the documentation for more info. Your error code is: UNKNOWN-API-LISTUSERS-GETNOTECOUNT"}) return } users = append(users, map[string]interface{}{"id": id, "username": username, "created": created, "space": space, "notes": notes}) } if err := rows.Err(); err != nil { - log.Println("[ERROR] Unknown in /api/listusers row iteration at", strconv.FormatInt(time.Now().Unix(), 10)+":", err) + log.Println("[ERROR] Unknown in /api/listusers row iteration:", err) } } }) diff --git a/schema.sql b/schema.sql index 45c7d4e..cb8378d 100644 --- a/schema.sql +++ b/schema.sql @@ -6,8 +6,7 @@ CREATE TABLE users ( id INTEGER PRIMARY KEY AUTOINCREMENT, created TEXT NOT NULL, username TEXT NOT NULL, - password TEXT NOT NULL, - versionTwoLegacyPassword TEXT NOT NULL DEFAULT 'nil', + password TEXT NOT NULL ); CREATE TABLE notes ( @@ -24,4 +23,11 @@ CREATE TABLE sessions ( session TEXT NOT NULL, id INTEGER NOT NULL, device TEXT NOT NULL DEFAULT '?' -); \ No newline at end of file +); + +CREATE TABLE oauth ( + id INTEGER NOT NULL, + oauthProvider TEXT NOT NULL, + encryptedPasswd TEXT NOT NULL, + UNIQUE (id, oauthProvider) +) \ No newline at end of file