auth_sqlite.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. package server
  2. import (
  3. "database/sql"
  4. "golang.org/x/crypto/bcrypt"
  5. )
  6. /*
  7. SELECT * FROM user;
  8. SELECT * FROM user_topic;
  9. INSERT INTO user VALUES ('phil','$2a$06$.4W0LI5mcxzxhpjUvpTaNeu0MhRO0T7B.CYnmAkRnlztIy7PrSODu', 'admin');
  10. INSERT INTO user VALUES ('ben','$2a$06$skJK/AecWCUmiCjr69ke.Ow/hFA616RdvJJPxnI221zyohsRlyXL.', 'user');
  11. INSERT INTO user VALUES ('marian','$2a$10$8U90swQIatvHHI4sw0Wo7.OUy6dUwzMcoOABi6BsS4uF0x3zcSXRW', 'user');
  12. INSERT INTO user_topic VALUES ('ben','alerts',1,1);
  13. INSERT INTO user_topic VALUES ('marian','alerts',1,0);
  14. INSERT INTO user_topic VALUES ('','announcements',1,0);
  15. INSERT INTO user_topic VALUES ('','write-all',1,1);
  16. ---
  17. dabbling for CLI
  18. ntfy user add phil --role=admin
  19. ntfy user del phil
  20. ntfy user change-pass phil
  21. ntfy user allow phil mytopic
  22. ntfy user allow phil mytopic --read-only
  23. ntfy user deny phil mytopic
  24. ntfy user list
  25. phil (admin)
  26. - read-write access to everything
  27. ben (user)
  28. - read-write access to a topic alerts
  29. - read access to
  30. everyone (no user)
  31. - read-only access to topic announcements
  32. */
  33. const (
  34. createAuthTablesQueries = `
  35. BEGIN;
  36. CREATE TABLE IF NOT EXISTS user (
  37. user TEXT NOT NULL PRIMARY KEY,
  38. pass TEXT NOT NULL,
  39. role TEXT NOT NULL
  40. );
  41. CREATE TABLE IF NOT EXISTS user_topic (
  42. user TEXT NOT NULL,
  43. topic TEXT NOT NULL,
  44. read INT NOT NULL,
  45. write INT NOT NULL,
  46. PRIMARY KEY (topic, user)
  47. );
  48. CREATE TABLE IF NOT EXISTS schema_version (
  49. id INT PRIMARY KEY,
  50. version INT NOT NULL
  51. );
  52. COMMIT;
  53. `
  54. selectUserQuery = `SELECT pass, role FROM user WHERE user = ?`
  55. selectTopicPermsQuery = `
  56. SELECT read, write
  57. FROM user_topic
  58. WHERE user IN ('', ?) AND topic = ?
  59. ORDER BY user DESC
  60. `
  61. )
  62. type sqliteAuth struct {
  63. db *sql.DB
  64. defaultRead bool
  65. defaultWrite bool
  66. }
  67. var _ auth = (*sqliteAuth)(nil)
  68. func newSqliteAuth(filename string, defaultRead, defaultWrite bool) (*sqliteAuth, error) {
  69. db, err := sql.Open("sqlite3", filename)
  70. if err != nil {
  71. return nil, err
  72. }
  73. if err := setupNewAuthDB(db); err != nil {
  74. return nil, err
  75. }
  76. return &sqliteAuth{
  77. db: db,
  78. defaultRead: defaultRead,
  79. defaultWrite: defaultWrite,
  80. }, nil
  81. }
  82. func setupNewAuthDB(db *sql.DB) error {
  83. if _, err := db.Exec(createAuthTablesQueries); err != nil {
  84. return err
  85. }
  86. // FIXME schema version
  87. return nil
  88. }
  89. func (a *sqliteAuth) Authenticate(username, password string) (*user, error) {
  90. rows, err := a.db.Query(selectUserQuery, username)
  91. if err != nil {
  92. return nil, err
  93. }
  94. defer rows.Close()
  95. var hash, role string
  96. if rows.Next() {
  97. if err := rows.Scan(&hash, &role); err != nil {
  98. return nil, err
  99. } else if err := rows.Err(); err != nil {
  100. return nil, err
  101. }
  102. }
  103. if err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)); err != nil {
  104. return nil, err
  105. }
  106. return &user{
  107. Name: username,
  108. Role: role,
  109. }, nil
  110. }
  111. func (a *sqliteAuth) Authorize(user *user, topic string, perm int) error {
  112. if user.Role == roleAdmin {
  113. return nil // Admin can do everything
  114. }
  115. // Select the read/write permissions for this user/topic combo. The query may return two
  116. // rows (one for everyone, and one for the user), but prioritizes the user. The value for
  117. // user.Name may be empty (= everyone).
  118. rows, err := a.db.Query(selectTopicPermsQuery, user.Name, topic)
  119. if err != nil {
  120. return err
  121. }
  122. defer rows.Close()
  123. if !rows.Next() {
  124. return a.resolvePerms(a.defaultRead, a.defaultWrite, perm)
  125. }
  126. var read, write bool
  127. if err := rows.Scan(&read, &write); err != nil {
  128. return err
  129. } else if err := rows.Err(); err != nil {
  130. return err
  131. }
  132. return a.resolvePerms(read, write, perm)
  133. }
  134. func (a *sqliteAuth) resolvePerms(read, write bool, perm int) error {
  135. if perm == permRead && read {
  136. return nil
  137. } else if perm == permWrite && write {
  138. return nil
  139. }
  140. return errHTTPUnauthorized
  141. }