You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
198 lines
4.3 KiB
198 lines
4.3 KiB
package data |
|
|
|
import ( |
|
"errors" |
|
"strings" |
|
"time" |
|
|
|
"github.com/owncast/owncast/models" |
|
log "github.com/sirupsen/logrus" |
|
) |
|
|
|
func createAccessTokensTable() { |
|
log.Traceln("Creating access_tokens table...") |
|
|
|
createTableSQL := `CREATE TABLE IF NOT EXISTS access_tokens ( |
|
"token" string NOT NULL PRIMARY KEY, |
|
"name" string, |
|
"scopes" TEXT, |
|
"timestamp" DATETIME DEFAULT CURRENT_TIMESTAMP, |
|
"last_used" DATETIME |
|
);` |
|
|
|
stmt, err := _db.Prepare(createTableSQL) |
|
if err != nil { |
|
log.Fatal(err) |
|
} |
|
defer stmt.Close() |
|
if _, err := stmt.Exec(); err != nil { |
|
log.Warnln(err) |
|
} |
|
} |
|
|
|
// InsertToken will add a new token to the database. |
|
func InsertToken(token string, name string, scopes []string) error { |
|
log.Println("Adding new access token:", name) |
|
|
|
scopesString := strings.Join(scopes, ",") |
|
|
|
tx, err := _db.Begin() |
|
if err != nil { |
|
return err |
|
} |
|
stmt, err := tx.Prepare("INSERT INTO access_tokens(token, name, scopes) values(?, ?, ?)") |
|
|
|
if err != nil { |
|
return err |
|
} |
|
defer stmt.Close() |
|
|
|
if _, err := stmt.Exec(token, name, scopesString); err != nil { |
|
return err |
|
} |
|
|
|
if err = tx.Commit(); err != nil { |
|
return err |
|
} |
|
|
|
return nil |
|
} |
|
|
|
// DeleteToken will delete a token from the database. |
|
func DeleteToken(token string) error { |
|
log.Println("Deleting access token:", token) |
|
|
|
tx, err := _db.Begin() |
|
if err != nil { |
|
return err |
|
} |
|
stmt, err := tx.Prepare("DELETE FROM access_tokens WHERE token = ?") |
|
|
|
if err != nil { |
|
return err |
|
} |
|
defer stmt.Close() |
|
|
|
result, err := stmt.Exec(token) |
|
if err != nil { |
|
return err |
|
} |
|
|
|
if rowsDeleted, _ := result.RowsAffected(); rowsDeleted == 0 { |
|
tx.Rollback() //nolint |
|
return errors.New(token + " not found") |
|
} |
|
|
|
if err = tx.Commit(); err != nil { |
|
return err |
|
} |
|
|
|
return nil |
|
} |
|
|
|
// DoesTokenSupportScope will determine if a specific token has access to perform a scoped action. |
|
func DoesTokenSupportScope(token string, scope string) (bool, error) { |
|
// This will split the scopes from comma separated to individual rows |
|
// so we can efficiently find if a token supports a single scope. |
|
// This is SQLite specific, so if we ever support other database |
|
// backends we need to support other methods. |
|
var query = `SELECT count(*) FROM ( |
|
WITH RECURSIVE split(token, scope, rest) AS ( |
|
SELECT token, '', scopes || ',' FROM access_tokens |
|
UNION ALL |
|
SELECT token, |
|
substr(rest, 0, instr(rest, ',')), |
|
substr(rest, instr(rest, ',')+1) |
|
FROM split |
|
WHERE rest <> '') |
|
SELECT token, scope |
|
FROM split |
|
WHERE scope <> '' |
|
ORDER BY token, scope |
|
) AS token WHERE token.token = ? AND token.scope = ?;` |
|
|
|
row := _db.QueryRow(query, token, scope) |
|
|
|
var count = 0 |
|
err := row.Scan(&count) |
|
|
|
return count > 0, err |
|
} |
|
|
|
// GetAccessTokens will return all access tokens. |
|
func GetAccessTokens() ([]models.AccessToken, error) { //nolint |
|
tokens := make([]models.AccessToken, 0) |
|
|
|
// Get all messages sent within the past day |
|
var query = "SELECT * FROM access_tokens" |
|
|
|
rows, err := _db.Query(query) |
|
if err != nil { |
|
return tokens, err |
|
} |
|
defer rows.Close() |
|
|
|
for rows.Next() { |
|
var token string |
|
var name string |
|
var scopes string |
|
var timestampString string |
|
var lastUsedString *string |
|
|
|
if err := rows.Scan(&token, &name, &scopes, ×tampString, &lastUsedString); err != nil { |
|
log.Error("There is a problem reading the database.", err) |
|
return tokens, err |
|
} |
|
|
|
timestamp, err := time.Parse(time.RFC3339, timestampString) |
|
if err != nil { |
|
return tokens, err |
|
} |
|
|
|
var lastUsed *time.Time = nil |
|
if lastUsedString != nil { |
|
lastUsedTime, _ := time.Parse(time.RFC3339, *lastUsedString) |
|
lastUsed = &lastUsedTime |
|
} |
|
|
|
singleToken := models.AccessToken{ |
|
Name: name, |
|
Token: token, |
|
Scopes: strings.Split(scopes, ","), |
|
Timestamp: timestamp, |
|
LastUsed: lastUsed, |
|
} |
|
|
|
tokens = append(tokens, singleToken) |
|
} |
|
|
|
if err := rows.Err(); err != nil { |
|
return tokens, err |
|
} |
|
|
|
return tokens, nil |
|
} |
|
|
|
// SetAccessTokenAsUsed will update the last used timestamp for a token. |
|
func SetAccessTokenAsUsed(token string) error { |
|
tx, err := _db.Begin() |
|
if err != nil { |
|
return err |
|
} |
|
stmt, err := tx.Prepare("UPDATE access_tokens SET last_used = CURRENT_TIMESTAMP WHERE token = ?") |
|
|
|
if err != nil { |
|
return err |
|
} |
|
defer stmt.Close() |
|
|
|
if _, err := stmt.Exec(token); err != nil { |
|
return err |
|
} |
|
|
|
if err = tx.Commit(); err != nil { |
|
return err |
|
} |
|
|
|
return nil |
|
}
|
|
|