manager.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  1. package user
  2. import (
  3. "database/sql"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. _ "github.com/mattn/go-sqlite3" // SQLite driver
  8. "golang.org/x/crypto/bcrypt"
  9. "heckel.io/ntfy/log"
  10. "heckel.io/ntfy/util"
  11. "strings"
  12. "sync"
  13. "time"
  14. )
  15. const (
  16. tokenLength = 32
  17. bcryptCost = 10
  18. intentionalSlowDownHash = "$2a$10$YFCQvqQDwIIwnJM1xkAYOeih0dg17UVGanaTStnrSzC8NCWxcLDwy" // Cost should match bcryptCost
  19. userStatsQueueWriterInterval = 33 * time.Second
  20. userTokenExpiryDuration = 72 * time.Hour
  21. )
  22. var (
  23. errNoTokenProvided = errors.New("no token provided")
  24. )
  25. // Manager-related queries
  26. const (
  27. createTablesQueriesNoTx = `
  28. CREATE TABLE IF NOT EXISTS plan (
  29. id INT NOT NULL,
  30. code TEXT NOT NULL,
  31. messages_limit INT NOT NULL,
  32. emails_limit INT NOT NULL,
  33. attachment_file_size_limit INT NOT NULL,
  34. attachment_total_size_limit INT NOT NULL,
  35. PRIMARY KEY (id)
  36. );
  37. CREATE TABLE IF NOT EXISTS user (
  38. id INTEGER PRIMARY KEY AUTOINCREMENT,
  39. plan_id INT,
  40. user TEXT NOT NULL,
  41. pass TEXT NOT NULL,
  42. role TEXT NOT NULL,
  43. messages INT NOT NULL DEFAULT (0),
  44. emails INT NOT NULL DEFAULT (0),
  45. settings JSON,
  46. FOREIGN KEY (plan_id) REFERENCES plan (id)
  47. );
  48. CREATE UNIQUE INDEX idx_user ON user (user);
  49. CREATE TABLE IF NOT EXISTS user_access (
  50. user_id INT NOT NULL,
  51. owner_user_id INT,
  52. topic TEXT NOT NULL,
  53. read INT NOT NULL,
  54. write INT NOT NULL,
  55. PRIMARY KEY (user_id, topic),
  56. FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE
  57. );
  58. CREATE TABLE IF NOT EXISTS user_token (
  59. user_id INT NOT NULL,
  60. token TEXT NOT NULL,
  61. expires INT NOT NULL,
  62. PRIMARY KEY (user_id, token),
  63. FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE
  64. );
  65. CREATE TABLE IF NOT EXISTS schemaVersion (
  66. id INT PRIMARY KEY,
  67. version INT NOT NULL
  68. );
  69. INSERT INTO user (id, user, pass, role) VALUES (1, '*', '', 'anonymous') ON CONFLICT (id) DO NOTHING;
  70. `
  71. createTablesQueries = `BEGIN; ` + createTablesQueriesNoTx + ` COMMIT;`
  72. selectUserByNameQuery = `
  73. SELECT u.user, u.pass, u.role, u.messages, u.emails, u.settings, p.code, p.messages_limit, p.emails_limit, p.attachment_file_size_limit, p.attachment_total_size_limit
  74. FROM user u
  75. LEFT JOIN plan p on p.id = u.plan_id
  76. WHERE user = ?
  77. `
  78. selectUserByTokenQuery = `
  79. SELECT u.user, u.pass, u.role, u.messages, u.emails, u.settings, p.code, p.messages_limit, p.emails_limit, p.attachment_file_size_limit, p.attachment_total_size_limit
  80. FROM user u
  81. JOIN user_token t on u.id = t.user_id
  82. LEFT JOIN plan p on p.id = u.plan_id
  83. WHERE t.token = ? AND t.expires >= ?
  84. `
  85. selectTopicPermsQuery = `
  86. SELECT read, write
  87. FROM user_access a
  88. JOIN user u ON u.id = a.user_id
  89. WHERE (u.user = '*' OR u.user = ?) AND ? LIKE a.topic
  90. ORDER BY u.user DESC
  91. `
  92. )
  93. // Manager-related queries
  94. const (
  95. insertUserQuery = `INSERT INTO user (user, pass, role) VALUES (?, ?, ?)`
  96. selectUsernamesQuery = `
  97. SELECT user
  98. FROM user
  99. ORDER BY
  100. CASE role
  101. WHEN 'admin' THEN 1
  102. WHEN 'anonymous' THEN 3
  103. ELSE 2
  104. END, user
  105. `
  106. updateUserPassQuery = `UPDATE user SET pass = ? WHERE user = ?`
  107. updateUserRoleQuery = `UPDATE user SET role = ? WHERE user = ?`
  108. updateUserSettingsQuery = `UPDATE user SET settings = ? WHERE user = ?`
  109. updateUserStatsQuery = `UPDATE user SET messages = ?, emails = ? WHERE user = ?`
  110. deleteUserQuery = `DELETE FROM user WHERE user = ?`
  111. upsertUserAccessQuery = `
  112. INSERT INTO user_access (user_id, topic, read, write)
  113. VALUES ((SELECT id FROM user WHERE user = ?), ?, ?, ?)
  114. ON CONFLICT (user_id, topic)
  115. DO UPDATE SET read=excluded.read, write=excluded.write
  116. `
  117. selectUserAccessQuery = `SELECT topic, read, write FROM user_access WHERE user_id = (SELECT id FROM user WHERE user = ?) ORDER BY write DESC, read DESC, topic`
  118. deleteAllAccessQuery = `DELETE FROM user_access`
  119. deleteUserAccessQuery = `DELETE FROM user_access WHERE user_id = (SELECT id FROM user WHERE user = ?)`
  120. deleteTopicAccessQuery = `DELETE FROM user_access WHERE user_id = (SELECT id FROM user WHERE user = ?) AND topic = ?`
  121. insertTokenQuery = `INSERT INTO user_token (user_id, token, expires) VALUES ((SELECT id FROM user WHERE user = ?), ?, ?)`
  122. updateTokenExpiryQuery = `UPDATE user_token SET expires = ? WHERE user_id = (SELECT id FROM user WHERE user = ?) AND token = ?`
  123. deleteTokenQuery = `DELETE FROM user_token WHERE user_id = (SELECT id FROM user WHERE user = ?) AND token = ?`
  124. deleteExpiredTokensQuery = `DELETE FROM user_token WHERE expires < ?`
  125. deleteUserTokensQuery = `DELETE FROM user_token WHERE user_id = (SELECT id FROM user WHERE user = ?)`
  126. )
  127. // Schema management queries
  128. const (
  129. currentSchemaVersion = 2
  130. insertSchemaVersion = `INSERT INTO schemaVersion VALUES (1, ?)`
  131. updateSchemaVersion = `UPDATE schemaVersion SET version = ? WHERE id = 1`
  132. selectSchemaVersionQuery = `SELECT version FROM schemaVersion WHERE id = 1`
  133. // 1 -> 2 (complex migration!)
  134. migrate1To2RenameUserTableQueryNoTx = `
  135. ALTER TABLE user RENAME TO user_old;
  136. `
  137. migrate1To2InsertFromOldTablesAndDropNoTx = `
  138. INSERT INTO user (user, pass, role)
  139. SELECT user, pass, role FROM user_old;
  140. INSERT INTO user_access (user_id, topic, read, write)
  141. SELECT u.id, a.topic, a.read, a.write
  142. FROM user u
  143. JOIN access a ON u.user = a.user;
  144. DROP TABLE access;
  145. DROP TABLE user_old;
  146. `
  147. )
  148. // Manager is an implementation of Manager. It stores users and access control list
  149. // in a SQLite database.
  150. type Manager struct {
  151. db *sql.DB
  152. defaultRead bool // Default read permission if no ACL matches
  153. defaultWrite bool // Default write permission if no ACL matches
  154. statsQueue map[string]*User // Username -> User, for "unimportant" user updates
  155. tokenExpiryInterval time.Duration // Duration after which tokens expire, and by which tokens are extended
  156. mu sync.Mutex
  157. }
  158. var _ Auther = (*Manager)(nil)
  159. // NewManager creates a new Manager instance
  160. func NewManager(filename string, defaultRead, defaultWrite bool) (*Manager, error) {
  161. return newManager(filename, defaultRead, defaultWrite, userTokenExpiryDuration, userStatsQueueWriterInterval)
  162. }
  163. // NewManager creates a new Manager instance
  164. func newManager(filename string, defaultRead, defaultWrite bool, tokenExpiryDuration, statsWriterInterval time.Duration) (*Manager, error) {
  165. db, err := sql.Open("sqlite3", filename)
  166. if err != nil {
  167. return nil, err
  168. }
  169. if err := setupDB(db); err != nil {
  170. return nil, err
  171. }
  172. manager := &Manager{
  173. db: db,
  174. defaultRead: defaultRead,
  175. defaultWrite: defaultWrite,
  176. statsQueue: make(map[string]*User),
  177. tokenExpiryInterval: tokenExpiryDuration,
  178. }
  179. go manager.userStatsQueueWriter(statsWriterInterval)
  180. return manager, nil
  181. }
  182. // Authenticate checks username and password and returns a User if correct. The method
  183. // returns in constant-ish time, regardless of whether the user exists or the password is
  184. // correct or incorrect.
  185. func (a *Manager) Authenticate(username, password string) (*User, error) {
  186. if username == Everyone {
  187. return nil, ErrUnauthenticated
  188. }
  189. user, err := a.User(username)
  190. if err != nil {
  191. bcrypt.CompareHashAndPassword([]byte(intentionalSlowDownHash),
  192. []byte("intentional slow-down to avoid timing attacks"))
  193. return nil, ErrUnauthenticated
  194. }
  195. if err := bcrypt.CompareHashAndPassword([]byte(user.Hash), []byte(password)); err != nil {
  196. return nil, ErrUnauthenticated
  197. }
  198. return user, nil
  199. }
  200. // AuthenticateToken checks if the token exists and returns the associated User if it does.
  201. // The method sets the User.Token value to the token that was used for authentication.
  202. func (a *Manager) AuthenticateToken(token string) (*User, error) {
  203. if len(token) != tokenLength {
  204. return nil, ErrUnauthenticated
  205. }
  206. user, err := a.userByToken(token)
  207. if err != nil {
  208. return nil, ErrUnauthenticated
  209. }
  210. user.Token = token
  211. return user, nil
  212. }
  213. // CreateToken generates a random token for the given user and returns it. The token expires
  214. // after a fixed duration unless ExtendToken is called.
  215. func (a *Manager) CreateToken(user *User) (*Token, error) {
  216. token, expires := util.RandomString(tokenLength), time.Now().Add(userTokenExpiryDuration)
  217. if _, err := a.db.Exec(insertTokenQuery, user.Name, token, expires.Unix()); err != nil {
  218. return nil, err
  219. }
  220. return &Token{
  221. Value: token,
  222. Expires: expires,
  223. }, nil
  224. }
  225. // ExtendToken sets the new expiry date for a token, thereby extending its use further into the future.
  226. func (a *Manager) ExtendToken(user *User) (*Token, error) {
  227. if user.Token == "" {
  228. return nil, errNoTokenProvided
  229. }
  230. newExpires := time.Now().Add(userTokenExpiryDuration)
  231. if _, err := a.db.Exec(updateTokenExpiryQuery, newExpires.Unix(), user.Name, user.Token); err != nil {
  232. return nil, err
  233. }
  234. return &Token{
  235. Value: user.Token,
  236. Expires: newExpires,
  237. }, nil
  238. }
  239. // RemoveToken deletes the token defined in User.Token
  240. func (a *Manager) RemoveToken(user *User) error {
  241. if user.Token == "" {
  242. return ErrUnauthorized
  243. }
  244. if _, err := a.db.Exec(deleteTokenQuery, user.Name, user.Token); err != nil {
  245. return err
  246. }
  247. return nil
  248. }
  249. // RemoveExpiredTokens deletes all expired tokens from the database
  250. func (a *Manager) RemoveExpiredTokens() error {
  251. if _, err := a.db.Exec(deleteExpiredTokensQuery, time.Now().Unix()); err != nil {
  252. return err
  253. }
  254. return nil
  255. }
  256. // ChangeSettings persists the user settings
  257. func (a *Manager) ChangeSettings(user *User) error {
  258. settings, err := json.Marshal(user.Prefs)
  259. if err != nil {
  260. return err
  261. }
  262. if _, err := a.db.Exec(updateUserSettingsQuery, string(settings), user.Name); err != nil {
  263. return err
  264. }
  265. return nil
  266. }
  267. // EnqueueStats adds the user to a queue which writes out user stats (messages, emails, ..) in
  268. // batches at a regular interval
  269. func (a *Manager) EnqueueStats(user *User) {
  270. a.mu.Lock()
  271. defer a.mu.Unlock()
  272. a.statsQueue[user.Name] = user
  273. }
  274. func (a *Manager) userStatsQueueWriter(interval time.Duration) {
  275. ticker := time.NewTicker(interval)
  276. for range ticker.C {
  277. if err := a.writeUserStatsQueue(); err != nil {
  278. log.Warn("UserManager: Writing user stats queue failed: %s", err.Error())
  279. }
  280. }
  281. }
  282. func (a *Manager) writeUserStatsQueue() error {
  283. a.mu.Lock()
  284. if len(a.statsQueue) == 0 {
  285. a.mu.Unlock()
  286. log.Trace("UserManager: No user stats updates to commit")
  287. return nil
  288. }
  289. statsQueue := a.statsQueue
  290. a.statsQueue = make(map[string]*User)
  291. a.mu.Unlock()
  292. tx, err := a.db.Begin()
  293. if err != nil {
  294. return err
  295. }
  296. defer tx.Rollback()
  297. log.Debug("UserManager: Writing user stats queue for %d user(s)", len(statsQueue))
  298. for username, u := range statsQueue {
  299. log.Trace("UserManager: Updating stats for user %s: messages=%d, emails=%d", username, u.Stats.Messages, u.Stats.Emails)
  300. if _, err := tx.Exec(updateUserStatsQuery, u.Stats.Messages, u.Stats.Emails, username); err != nil {
  301. return err
  302. }
  303. }
  304. return tx.Commit()
  305. }
  306. // Authorize returns nil if the given user has access to the given topic using the desired
  307. // permission. The user param may be nil to signal an anonymous user.
  308. func (a *Manager) Authorize(user *User, topic string, perm Permission) error {
  309. if user != nil && user.Role == RoleAdmin {
  310. return nil // Admin can do everything
  311. }
  312. username := Everyone
  313. if user != nil {
  314. username = user.Name
  315. }
  316. // Select the read/write permissions for this user/topic combo. The query may return two
  317. // rows (one for everyone, and one for the user), but prioritizes the user. The value for
  318. // user.Name may be empty (= everyone).
  319. rows, err := a.db.Query(selectTopicPermsQuery, username, topic)
  320. if err != nil {
  321. return err
  322. }
  323. defer rows.Close()
  324. if !rows.Next() {
  325. return a.resolvePerms(a.defaultRead, a.defaultWrite, perm)
  326. }
  327. var read, write bool
  328. if err := rows.Scan(&read, &write); err != nil {
  329. return err
  330. } else if err := rows.Err(); err != nil {
  331. return err
  332. }
  333. return a.resolvePerms(read, write, perm)
  334. }
  335. func (a *Manager) resolvePerms(read, write bool, perm Permission) error {
  336. if perm == PermissionRead && read {
  337. return nil
  338. } else if perm == PermissionWrite && write {
  339. return nil
  340. }
  341. return ErrUnauthorized
  342. }
  343. // AddUser adds a user with the given username, password and role
  344. func (a *Manager) AddUser(username, password string, role Role) error {
  345. if !AllowedUsername(username) || !AllowedRole(role) {
  346. return ErrInvalidArgument
  347. }
  348. hash, err := bcrypt.GenerateFromPassword([]byte(password), bcryptCost)
  349. if err != nil {
  350. return err
  351. }
  352. if _, err = a.db.Exec(insertUserQuery, username, hash, role); err != nil {
  353. return err
  354. }
  355. return nil
  356. }
  357. // RemoveUser deletes the user with the given username. The function returns nil on success, even
  358. // if the user did not exist in the first place.
  359. func (a *Manager) RemoveUser(username string) error {
  360. if !AllowedUsername(username) {
  361. return ErrInvalidArgument
  362. }
  363. tx, err := a.db.Begin()
  364. if err != nil {
  365. return err
  366. }
  367. defer tx.Rollback()
  368. if _, err := tx.Exec(deleteUserAccessQuery, username); err != nil {
  369. return err
  370. }
  371. if _, err := tx.Exec(deleteUserTokensQuery, username); err != nil {
  372. return err
  373. }
  374. if _, err := tx.Exec(deleteUserQuery, username); err != nil {
  375. return err
  376. }
  377. return tx.Commit()
  378. }
  379. // Users returns a list of users. It always also returns the Everyone user ("*").
  380. func (a *Manager) Users() ([]*User, error) {
  381. rows, err := a.db.Query(selectUsernamesQuery)
  382. if err != nil {
  383. return nil, err
  384. }
  385. defer rows.Close()
  386. usernames := make([]string, 0)
  387. for rows.Next() {
  388. var username string
  389. if err := rows.Scan(&username); err != nil {
  390. return nil, err
  391. } else if err := rows.Err(); err != nil {
  392. return nil, err
  393. }
  394. usernames = append(usernames, username)
  395. }
  396. rows.Close()
  397. users := make([]*User, 0)
  398. for _, username := range usernames {
  399. user, err := a.User(username)
  400. if err != nil {
  401. return nil, err
  402. }
  403. users = append(users, user)
  404. }
  405. return users, nil
  406. }
  407. // User returns the user with the given username if it exists, or ErrNotFound otherwise.
  408. // You may also pass Everyone to retrieve the anonymous user and its Grant list.
  409. func (a *Manager) User(username string) (*User, error) {
  410. rows, err := a.db.Query(selectUserByNameQuery, username)
  411. if err != nil {
  412. return nil, err
  413. }
  414. return a.readUser(rows)
  415. }
  416. func (a *Manager) userByToken(token string) (*User, error) {
  417. rows, err := a.db.Query(selectUserByTokenQuery, token, time.Now().Unix())
  418. if err != nil {
  419. return nil, err
  420. }
  421. return a.readUser(rows)
  422. }
  423. func (a *Manager) readUser(rows *sql.Rows) (*User, error) {
  424. defer rows.Close()
  425. var username, hash, role string
  426. var settings, planCode sql.NullString
  427. var messages, emails int64
  428. var messagesLimit, emailsLimit, attachmentFileSizeLimit, attachmentTotalSizeLimit sql.NullInt64
  429. if !rows.Next() {
  430. return nil, ErrNotFound
  431. }
  432. if err := rows.Scan(&username, &hash, &role, &messages, &emails, &settings, &planCode, &messagesLimit, &emailsLimit, &attachmentFileSizeLimit, &attachmentTotalSizeLimit); err != nil {
  433. return nil, err
  434. } else if err := rows.Err(); err != nil {
  435. return nil, err
  436. }
  437. grants, err := a.readGrants(username)
  438. if err != nil {
  439. return nil, err
  440. }
  441. user := &User{
  442. Name: username,
  443. Hash: hash,
  444. Role: Role(role),
  445. Grants: grants,
  446. Stats: &Stats{
  447. Messages: messages,
  448. Emails: emails,
  449. },
  450. }
  451. if settings.Valid {
  452. user.Prefs = &Prefs{}
  453. if err := json.Unmarshal([]byte(settings.String), user.Prefs); err != nil {
  454. return nil, err
  455. }
  456. }
  457. if planCode.Valid {
  458. user.Plan = &Plan{
  459. Code: planCode.String,
  460. Upgradable: true, // FIXME
  461. MessagesLimit: messagesLimit.Int64,
  462. EmailsLimit: emailsLimit.Int64,
  463. AttachmentFileSizeLimit: attachmentFileSizeLimit.Int64,
  464. AttachmentTotalSizeLimit: attachmentTotalSizeLimit.Int64,
  465. }
  466. }
  467. return user, nil
  468. }
  469. func (a *Manager) readGrants(username string) ([]Grant, error) {
  470. rows, err := a.db.Query(selectUserAccessQuery, username)
  471. if err != nil {
  472. return nil, err
  473. }
  474. defer rows.Close()
  475. grants := make([]Grant, 0)
  476. for rows.Next() {
  477. var topic string
  478. var read, write bool
  479. if err := rows.Scan(&topic, &read, &write); err != nil {
  480. return nil, err
  481. } else if err := rows.Err(); err != nil {
  482. return nil, err
  483. }
  484. grants = append(grants, Grant{
  485. TopicPattern: fromSQLWildcard(topic),
  486. AllowRead: read,
  487. AllowWrite: write,
  488. })
  489. }
  490. return grants, nil
  491. }
  492. // ChangePassword changes a user's password
  493. func (a *Manager) ChangePassword(username, password string) error {
  494. hash, err := bcrypt.GenerateFromPassword([]byte(password), bcryptCost)
  495. if err != nil {
  496. return err
  497. }
  498. if _, err := a.db.Exec(updateUserPassQuery, hash, username); err != nil {
  499. return err
  500. }
  501. return nil
  502. }
  503. // ChangeRole changes a user's role. When a role is changed from RoleUser to RoleAdmin,
  504. // all existing access control entries (Grant) are removed, since they are no longer needed.
  505. func (a *Manager) ChangeRole(username string, role Role) error {
  506. if !AllowedUsername(username) || !AllowedRole(role) {
  507. return ErrInvalidArgument
  508. }
  509. if _, err := a.db.Exec(updateUserRoleQuery, string(role), username); err != nil {
  510. return err
  511. }
  512. if role == RoleAdmin {
  513. if _, err := a.db.Exec(deleteUserAccessQuery, username); err != nil {
  514. return err
  515. }
  516. }
  517. return nil
  518. }
  519. // AllowAccess adds or updates an entry in th access control list for a specific user. It controls
  520. // read/write access to a topic. The parameter topicPattern may include wildcards (*).
  521. func (a *Manager) AllowAccess(username string, topicPattern string, read bool, write bool) error {
  522. if (!AllowedUsername(username) && username != Everyone) || !AllowedTopicPattern(topicPattern) {
  523. return ErrInvalidArgument
  524. }
  525. if _, err := a.db.Exec(upsertUserAccessQuery, username, toSQLWildcard(topicPattern), read, write); err != nil {
  526. return err
  527. }
  528. return nil
  529. }
  530. // ResetAccess removes an access control list entry for a specific username/topic, or (if topic is
  531. // empty) for an entire user. The parameter topicPattern may include wildcards (*).
  532. func (a *Manager) ResetAccess(username string, topicPattern string) error {
  533. if !AllowedUsername(username) && username != Everyone && username != "" {
  534. return ErrInvalidArgument
  535. } else if !AllowedTopicPattern(topicPattern) && topicPattern != "" {
  536. return ErrInvalidArgument
  537. }
  538. if username == "" && topicPattern == "" {
  539. _, err := a.db.Exec(deleteAllAccessQuery, username)
  540. return err
  541. } else if topicPattern == "" {
  542. _, err := a.db.Exec(deleteUserAccessQuery, username)
  543. return err
  544. }
  545. _, err := a.db.Exec(deleteTopicAccessQuery, username, toSQLWildcard(topicPattern))
  546. return err
  547. }
  548. // DefaultAccess returns the default read/write access if no access control entry matches
  549. func (a *Manager) DefaultAccess() (read bool, write bool) {
  550. return a.defaultRead, a.defaultWrite
  551. }
  552. func toSQLWildcard(s string) string {
  553. return strings.ReplaceAll(s, "*", "%")
  554. }
  555. func fromSQLWildcard(s string) string {
  556. return strings.ReplaceAll(s, "%", "*")
  557. }
  558. func setupDB(db *sql.DB) error {
  559. // If 'schemaVersion' table does not exist, this must be a new database
  560. rowsSV, err := db.Query(selectSchemaVersionQuery)
  561. if err != nil {
  562. return setupNewDB(db)
  563. }
  564. defer rowsSV.Close()
  565. // If 'schemaVersion' table exists, read version and potentially upgrade
  566. schemaVersion := 0
  567. if !rowsSV.Next() {
  568. return errors.New("cannot determine schema version: database file may be corrupt")
  569. }
  570. if err := rowsSV.Scan(&schemaVersion); err != nil {
  571. return err
  572. }
  573. rowsSV.Close()
  574. // Do migrations
  575. if schemaVersion == currentSchemaVersion {
  576. return nil
  577. } else if schemaVersion == 1 {
  578. return migrateFrom1(db)
  579. }
  580. return fmt.Errorf("unexpected schema version found: %d", schemaVersion)
  581. }
  582. func setupNewDB(db *sql.DB) error {
  583. if _, err := db.Exec(createTablesQueries); err != nil {
  584. return err
  585. }
  586. if _, err := db.Exec(insertSchemaVersion, currentSchemaVersion); err != nil {
  587. return err
  588. }
  589. return nil
  590. }
  591. func migrateFrom1(db *sql.DB) error {
  592. log.Info("Migrating user database schema: from 1 to 2")
  593. tx, err := db.Begin()
  594. if err != nil {
  595. return err
  596. }
  597. defer tx.Rollback()
  598. if _, err := tx.Exec(migrate1To2RenameUserTableQueryNoTx); err != nil {
  599. return err
  600. }
  601. if _, err := tx.Exec(createTablesQueriesNoTx); err != nil {
  602. return err
  603. }
  604. if _, err := tx.Exec(migrate1To2InsertFromOldTablesAndDropNoTx); err != nil {
  605. return err
  606. }
  607. if _, err := tx.Exec(updateSchemaVersion, 2); err != nil {
  608. return err
  609. }
  610. if err := tx.Commit(); err != nil {
  611. return err
  612. }
  613. return nil // Update this when a new version is added
  614. }