manager.go 23 KB

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