manager.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  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. defaultAccess Permission // Default permission if no ACL matches
  174. statsQueue map[string]*User // Username -> User, for "unimportant" user updates
  175. tokenExpiryInterval time.Duration // Duration after which tokens expire, and by which tokens are extended
  176. mu sync.Mutex
  177. }
  178. var _ Auther = (*Manager)(nil)
  179. // NewManager creates a new Manager instance
  180. func NewManager(filename string, defaultAccess Permission) (*Manager, error) {
  181. return newManager(filename, defaultAccess, userTokenExpiryDuration, userStatsQueueWriterInterval)
  182. }
  183. // NewManager creates a new Manager instance
  184. func newManager(filename string, defaultAccess Permission, tokenExpiryDuration, statsWriterInterval time.Duration) (*Manager, error) {
  185. db, err := sql.Open("sqlite3", filename)
  186. if err != nil {
  187. return nil, err
  188. }
  189. if err := setupDB(db); err != nil {
  190. return nil, err
  191. }
  192. manager := &Manager{
  193. db: db,
  194. defaultAccess: defaultAccess,
  195. statsQueue: make(map[string]*User),
  196. tokenExpiryInterval: tokenExpiryDuration,
  197. }
  198. go manager.userStatsQueueWriter(statsWriterInterval)
  199. return manager, nil
  200. }
  201. // Authenticate checks username and password and returns a User if correct. The method
  202. // returns in constant-ish time, regardless of whether the user exists or the password is
  203. // correct or incorrect.
  204. func (a *Manager) Authenticate(username, password string) (*User, error) {
  205. if username == Everyone {
  206. return nil, ErrUnauthenticated
  207. }
  208. user, err := a.User(username)
  209. if err != nil {
  210. bcrypt.CompareHashAndPassword([]byte(intentionalSlowDownHash),
  211. []byte("intentional slow-down to avoid timing attacks"))
  212. return nil, ErrUnauthenticated
  213. }
  214. if err := bcrypt.CompareHashAndPassword([]byte(user.Hash), []byte(password)); err != nil {
  215. return nil, ErrUnauthenticated
  216. }
  217. return user, nil
  218. }
  219. // AuthenticateToken checks if the token exists and returns the associated User if it does.
  220. // The method sets the User.Token value to the token that was used for authentication.
  221. func (a *Manager) AuthenticateToken(token string) (*User, error) {
  222. if len(token) != tokenLength {
  223. return nil, ErrUnauthenticated
  224. }
  225. user, err := a.userByToken(token)
  226. if err != nil {
  227. return nil, ErrUnauthenticated
  228. }
  229. user.Token = token
  230. return user, nil
  231. }
  232. // CreateToken generates a random token for the given user and returns it. The token expires
  233. // after a fixed duration unless ExtendToken is called.
  234. func (a *Manager) CreateToken(user *User) (*Token, error) {
  235. token, expires := util.RandomString(tokenLength), time.Now().Add(userTokenExpiryDuration)
  236. if _, err := a.db.Exec(insertTokenQuery, user.Name, token, expires.Unix()); err != nil {
  237. return nil, err
  238. }
  239. return &Token{
  240. Value: token,
  241. Expires: expires,
  242. }, nil
  243. }
  244. // ExtendToken sets the new expiry date for a token, thereby extending its use further into the future.
  245. func (a *Manager) ExtendToken(user *User) (*Token, error) {
  246. if user.Token == "" {
  247. return nil, errNoTokenProvided
  248. }
  249. newExpires := time.Now().Add(userTokenExpiryDuration)
  250. if _, err := a.db.Exec(updateTokenExpiryQuery, newExpires.Unix(), user.Name, user.Token); err != nil {
  251. return nil, err
  252. }
  253. return &Token{
  254. Value: user.Token,
  255. Expires: newExpires,
  256. }, nil
  257. }
  258. // RemoveToken deletes the token defined in User.Token
  259. func (a *Manager) RemoveToken(user *User) error {
  260. if user.Token == "" {
  261. return ErrUnauthorized
  262. }
  263. if _, err := a.db.Exec(deleteTokenQuery, user.Name, user.Token); err != nil {
  264. return err
  265. }
  266. return nil
  267. }
  268. // RemoveExpiredTokens deletes all expired tokens from the database
  269. func (a *Manager) RemoveExpiredTokens() error {
  270. if _, err := a.db.Exec(deleteExpiredTokensQuery, time.Now().Unix()); err != nil {
  271. return err
  272. }
  273. return nil
  274. }
  275. // ChangeSettings persists the user settings
  276. func (a *Manager) ChangeSettings(user *User) error {
  277. settings, err := json.Marshal(user.Prefs)
  278. if err != nil {
  279. return err
  280. }
  281. if _, err := a.db.Exec(updateUserSettingsQuery, string(settings), user.Name); err != nil {
  282. return err
  283. }
  284. return nil
  285. }
  286. // EnqueueStats adds the user to a queue which writes out user stats (messages, emails, ..) in
  287. // batches at a regular interval
  288. func (a *Manager) EnqueueStats(user *User) {
  289. a.mu.Lock()
  290. defer a.mu.Unlock()
  291. a.statsQueue[user.Name] = user
  292. }
  293. func (a *Manager) userStatsQueueWriter(interval time.Duration) {
  294. ticker := time.NewTicker(interval)
  295. for range ticker.C {
  296. if err := a.writeUserStatsQueue(); err != nil {
  297. log.Warn("UserManager: Writing user stats queue failed: %s", err.Error())
  298. }
  299. }
  300. }
  301. func (a *Manager) writeUserStatsQueue() error {
  302. a.mu.Lock()
  303. if len(a.statsQueue) == 0 {
  304. a.mu.Unlock()
  305. log.Trace("UserManager: No user stats updates to commit")
  306. return nil
  307. }
  308. statsQueue := a.statsQueue
  309. a.statsQueue = make(map[string]*User)
  310. a.mu.Unlock()
  311. tx, err := a.db.Begin()
  312. if err != nil {
  313. return err
  314. }
  315. defer tx.Rollback()
  316. log.Debug("UserManager: Writing user stats queue for %d user(s)", len(statsQueue))
  317. for username, u := range statsQueue {
  318. log.Trace("UserManager: Updating stats for user %s: messages=%d, emails=%d", username, u.Stats.Messages, u.Stats.Emails)
  319. if _, err := tx.Exec(updateUserStatsQuery, u.Stats.Messages, u.Stats.Emails, username); err != nil {
  320. return err
  321. }
  322. }
  323. return tx.Commit()
  324. }
  325. // Authorize returns nil if the given user has access to the given topic using the desired
  326. // permission. The user param may be nil to signal an anonymous user.
  327. func (a *Manager) Authorize(user *User, topic string, perm Permission) error {
  328. if user != nil && user.Role == RoleAdmin {
  329. return nil // Admin can do everything
  330. }
  331. username := Everyone
  332. if user != nil {
  333. username = user.Name
  334. }
  335. // Select the read/write permissions for this user/topic combo. The query may return two
  336. // rows (one for everyone, and one for the user), but prioritizes the user.
  337. rows, err := a.db.Query(selectTopicPermsQuery, Everyone, username, topic)
  338. if err != nil {
  339. return err
  340. }
  341. defer rows.Close()
  342. if !rows.Next() {
  343. return a.resolvePerms(a.defaultAccess, perm)
  344. }
  345. var read, write bool
  346. if err := rows.Scan(&read, &write); err != nil {
  347. return err
  348. } else if err := rows.Err(); err != nil {
  349. return err
  350. }
  351. return a.resolvePerms(NewPermission(read, write), perm)
  352. }
  353. func (a *Manager) resolvePerms(base, perm Permission) error {
  354. if perm == PermissionRead && base.IsRead() {
  355. return nil
  356. } else if perm == PermissionWrite && base.IsWrite() {
  357. return nil
  358. }
  359. return ErrUnauthorized
  360. }
  361. // AddUser adds a user with the given username, password and role
  362. func (a *Manager) AddUser(username, password string, role Role) error {
  363. if !AllowedUsername(username) || !AllowedRole(role) {
  364. return ErrInvalidArgument
  365. }
  366. hash, err := bcrypt.GenerateFromPassword([]byte(password), bcryptCost)
  367. if err != nil {
  368. return err
  369. }
  370. if _, err = a.db.Exec(insertUserQuery, username, hash, role); err != nil {
  371. return err
  372. }
  373. return nil
  374. }
  375. // RemoveUser deletes the user with the given username. The function returns nil on success, even
  376. // if the user did not exist in the first place.
  377. func (a *Manager) RemoveUser(username string) error {
  378. if !AllowedUsername(username) {
  379. return ErrInvalidArgument
  380. }
  381. tx, err := a.db.Begin()
  382. if err != nil {
  383. return err
  384. }
  385. defer tx.Rollback()
  386. if _, err := tx.Exec(deleteUserAccessQuery, username); err != nil {
  387. return err
  388. }
  389. if _, err := tx.Exec(deleteUserTokensQuery, username); err != nil {
  390. return err
  391. }
  392. if _, err := tx.Exec(deleteUserQuery, username); err != nil {
  393. return err
  394. }
  395. return tx.Commit()
  396. }
  397. // Users returns a list of users. It always also returns the Everyone user ("*").
  398. func (a *Manager) Users() ([]*User, error) {
  399. rows, err := a.db.Query(selectUsernamesQuery)
  400. if err != nil {
  401. return nil, err
  402. }
  403. defer rows.Close()
  404. usernames := make([]string, 0)
  405. for rows.Next() {
  406. var username string
  407. if err := rows.Scan(&username); err != nil {
  408. return nil, err
  409. } else if err := rows.Err(); err != nil {
  410. return nil, err
  411. }
  412. usernames = append(usernames, username)
  413. }
  414. rows.Close()
  415. users := make([]*User, 0)
  416. for _, username := range usernames {
  417. user, err := a.User(username)
  418. if err != nil {
  419. return nil, err
  420. }
  421. users = append(users, user)
  422. }
  423. return users, nil
  424. }
  425. // User returns the user with the given username if it exists, or ErrNotFound otherwise.
  426. // You may also pass Everyone to retrieve the anonymous user and its Grant list.
  427. func (a *Manager) User(username string) (*User, error) {
  428. rows, err := a.db.Query(selectUserByNameQuery, username)
  429. if err != nil {
  430. return nil, err
  431. }
  432. return a.readUser(rows)
  433. }
  434. func (a *Manager) userByToken(token string) (*User, error) {
  435. rows, err := a.db.Query(selectUserByTokenQuery, token, time.Now().Unix())
  436. if err != nil {
  437. return nil, err
  438. }
  439. return a.readUser(rows)
  440. }
  441. func (a *Manager) readUser(rows *sql.Rows) (*User, error) {
  442. defer rows.Close()
  443. var username, hash, role string
  444. var settings, planCode sql.NullString
  445. var messages, emails int64
  446. var messagesLimit, emailsLimit, topicsLimit, attachmentFileSizeLimit, attachmentTotalSizeLimit sql.NullInt64
  447. if !rows.Next() {
  448. return nil, ErrNotFound
  449. }
  450. if err := rows.Scan(&username, &hash, &role, &messages, &emails, &settings, &planCode, &messagesLimit, &emailsLimit, &topicsLimit, &attachmentFileSizeLimit, &attachmentTotalSizeLimit); err != nil {
  451. return nil, err
  452. } else if err := rows.Err(); err != nil {
  453. return nil, err
  454. }
  455. user := &User{
  456. Name: username,
  457. Hash: hash,
  458. Role: Role(role),
  459. Stats: &Stats{
  460. Messages: messages,
  461. Emails: emails,
  462. },
  463. }
  464. if settings.Valid {
  465. user.Prefs = &Prefs{}
  466. if err := json.Unmarshal([]byte(settings.String), user.Prefs); err != nil {
  467. return nil, err
  468. }
  469. }
  470. if planCode.Valid {
  471. user.Plan = &Plan{
  472. Code: planCode.String,
  473. Upgradable: true, // FIXME
  474. MessagesLimit: messagesLimit.Int64,
  475. EmailsLimit: emailsLimit.Int64,
  476. TopicsLimit: topicsLimit.Int64,
  477. AttachmentFileSizeLimit: attachmentFileSizeLimit.Int64,
  478. AttachmentTotalSizeLimit: attachmentTotalSizeLimit.Int64,
  479. }
  480. }
  481. return user, nil
  482. }
  483. // Grants returns all user-specific access control entries
  484. func (a *Manager) Grants(username string) ([]Grant, error) {
  485. rows, err := a.db.Query(selectUserAccessQuery, username)
  486. if err != nil {
  487. return nil, err
  488. }
  489. defer rows.Close()
  490. grants := make([]Grant, 0)
  491. for rows.Next() {
  492. var topic string
  493. var read, write bool
  494. if err := rows.Scan(&topic, &read, &write); err != nil {
  495. return nil, err
  496. } else if err := rows.Err(); err != nil {
  497. return nil, err
  498. }
  499. grants = append(grants, Grant{
  500. TopicPattern: fromSQLWildcard(topic),
  501. Allow: NewPermission(read, write),
  502. })
  503. }
  504. return grants, nil
  505. }
  506. // Reservations returns all user-owned topics, and the associated everyone-access
  507. func (a *Manager) Reservations(username string) ([]Reservation, error) {
  508. rows, err := a.db.Query(selectUserReservationsQuery, Everyone, username)
  509. if err != nil {
  510. return nil, err
  511. }
  512. defer rows.Close()
  513. reservations := make([]Reservation, 0)
  514. for rows.Next() {
  515. var topic string
  516. var ownerRead, ownerWrite bool
  517. var everyoneRead, everyoneWrite sql.NullBool
  518. if err := rows.Scan(&topic, &ownerRead, &ownerWrite, &everyoneRead, &everyoneWrite); err != nil {
  519. return nil, err
  520. } else if err := rows.Err(); err != nil {
  521. return nil, err
  522. }
  523. reservations = append(reservations, Reservation{
  524. Topic: topic,
  525. Owner: NewPermission(ownerRead, ownerWrite),
  526. Everyone: NewPermission(everyoneRead.Bool, everyoneWrite.Bool), // false if null
  527. })
  528. }
  529. return reservations, nil
  530. }
  531. // ChangePassword changes a user's password
  532. func (a *Manager) ChangePassword(username, password string) error {
  533. hash, err := bcrypt.GenerateFromPassword([]byte(password), bcryptCost)
  534. if err != nil {
  535. return err
  536. }
  537. if _, err := a.db.Exec(updateUserPassQuery, hash, username); err != nil {
  538. return err
  539. }
  540. return nil
  541. }
  542. // ChangeRole changes a user's role. When a role is changed from RoleUser to RoleAdmin,
  543. // all existing access control entries (Grant) are removed, since they are no longer needed.
  544. func (a *Manager) ChangeRole(username string, role Role) error {
  545. if !AllowedUsername(username) || !AllowedRole(role) {
  546. return ErrInvalidArgument
  547. }
  548. if _, err := a.db.Exec(updateUserRoleQuery, string(role), username); err != nil {
  549. return err
  550. }
  551. if role == RoleAdmin {
  552. if _, err := a.db.Exec(deleteUserAccessQuery, username); err != nil {
  553. return err
  554. }
  555. }
  556. return nil
  557. }
  558. // CheckAllowAccess tests if a user may create an access control entry for the given topic.
  559. // If there are any ACL entries that are not owned by the user, an error is returned.
  560. func (a *Manager) CheckAllowAccess(username string, topic string) error {
  561. if (!AllowedUsername(username) && username != Everyone) || !AllowedTopic(topic) {
  562. return ErrInvalidArgument
  563. }
  564. rows, err := a.db.Query(selectOtherAccessCountQuery, topic, topic, username)
  565. if err != nil {
  566. return err
  567. }
  568. defer rows.Close()
  569. if !rows.Next() {
  570. return errors.New("no rows found")
  571. }
  572. var otherCount int
  573. if err := rows.Scan(&otherCount); err != nil {
  574. return err
  575. }
  576. if otherCount > 0 {
  577. return errTopicOwnedByOthers
  578. }
  579. return nil
  580. }
  581. // AllowAccess adds or updates an entry in th access control list for a specific user. It controls
  582. // read/write access to a topic. The parameter topicPattern may include wildcards (*). The ACL entry
  583. // owner may either be a user (username), or the system (empty).
  584. func (a *Manager) AllowAccess(owner, username string, topicPattern string, read bool, write bool) error {
  585. if !AllowedUsername(username) && username != Everyone {
  586. return ErrInvalidArgument
  587. } else if owner != "" && !AllowedUsername(owner) {
  588. return ErrInvalidArgument
  589. } else if !AllowedTopicPattern(topicPattern) {
  590. return ErrInvalidArgument
  591. }
  592. if _, err := a.db.Exec(upsertUserAccessQuery, username, toSQLWildcard(topicPattern), read, write, owner, owner); err != nil {
  593. return err
  594. }
  595. return nil
  596. }
  597. // ResetAccess removes an access control list entry for a specific username/topic, or (if topic is
  598. // empty) for an entire user. The parameter topicPattern may include wildcards (*).
  599. func (a *Manager) ResetAccess(username string, topicPattern string) error {
  600. if !AllowedUsername(username) && username != Everyone && username != "" {
  601. return ErrInvalidArgument
  602. } else if !AllowedTopicPattern(topicPattern) && topicPattern != "" {
  603. return ErrInvalidArgument
  604. }
  605. if username == "" && topicPattern == "" {
  606. _, err := a.db.Exec(deleteAllAccessQuery, username)
  607. return err
  608. } else if topicPattern == "" {
  609. _, err := a.db.Exec(deleteUserAccessQuery, username)
  610. return err
  611. }
  612. _, err := a.db.Exec(deleteTopicAccessQuery, username, toSQLWildcard(topicPattern))
  613. return err
  614. }
  615. // DefaultAccess returns the default read/write access if no access control entry matches
  616. func (a *Manager) DefaultAccess() Permission {
  617. return a.defaultAccess
  618. }
  619. func toSQLWildcard(s string) string {
  620. return strings.ReplaceAll(s, "*", "%")
  621. }
  622. func fromSQLWildcard(s string) string {
  623. return strings.ReplaceAll(s, "%", "*")
  624. }
  625. func setupDB(db *sql.DB) error {
  626. // If 'schemaVersion' table does not exist, this must be a new database
  627. rowsSV, err := db.Query(selectSchemaVersionQuery)
  628. if err != nil {
  629. return setupNewDB(db)
  630. }
  631. defer rowsSV.Close()
  632. // If 'schemaVersion' table exists, read version and potentially upgrade
  633. schemaVersion := 0
  634. if !rowsSV.Next() {
  635. return errors.New("cannot determine schema version: database file may be corrupt")
  636. }
  637. if err := rowsSV.Scan(&schemaVersion); err != nil {
  638. return err
  639. }
  640. rowsSV.Close()
  641. // Do migrations
  642. if schemaVersion == currentSchemaVersion {
  643. return nil
  644. } else if schemaVersion == 1 {
  645. return migrateFrom1(db)
  646. }
  647. return fmt.Errorf("unexpected schema version found: %d", schemaVersion)
  648. }
  649. func setupNewDB(db *sql.DB) error {
  650. if _, err := db.Exec(createTablesQueries); err != nil {
  651. return err
  652. }
  653. if _, err := db.Exec(insertSchemaVersion, currentSchemaVersion); err != nil {
  654. return err
  655. }
  656. return nil
  657. }
  658. func migrateFrom1(db *sql.DB) error {
  659. log.Info("Migrating user database schema: from 1 to 2")
  660. tx, err := db.Begin()
  661. if err != nil {
  662. return err
  663. }
  664. defer tx.Rollback()
  665. if _, err := tx.Exec(migrate1To2RenameUserTableQueryNoTx); err != nil {
  666. return err
  667. }
  668. if _, err := tx.Exec(createTablesQueriesNoTx); err != nil {
  669. return err
  670. }
  671. if _, err := tx.Exec(migrate1To2InsertFromOldTablesAndDropNoTx); err != nil {
  672. return err
  673. }
  674. if _, err := tx.Exec(updateSchemaVersion, 2); err != nil {
  675. return err
  676. }
  677. if err := tx.Commit(); err != nil {
  678. return err
  679. }
  680. return nil // Update this when a new version is added
  681. }