manager.go 25 KB

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