manager.go 25 KB

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