manager.go 23 KB

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