auth_sqlite.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. package auth
  2. import (
  3. "database/sql"
  4. _ "github.com/mattn/go-sqlite3" // SQLite driver
  5. "golang.org/x/crypto/bcrypt"
  6. "regexp"
  7. )
  8. const (
  9. bcryptCost = 11
  10. )
  11. // Auther-related queries
  12. const (
  13. createAuthTablesQueries = `
  14. BEGIN;
  15. CREATE TABLE IF NOT EXISTS user (
  16. user TEXT NOT NULL PRIMARY KEY,
  17. pass TEXT NOT NULL,
  18. role TEXT NOT NULL
  19. );
  20. CREATE TABLE IF NOT EXISTS access (
  21. user TEXT NOT NULL,
  22. topic TEXT NOT NULL,
  23. read INT NOT NULL,
  24. write INT NOT NULL,
  25. PRIMARY KEY (topic, user)
  26. );
  27. CREATE TABLE IF NOT EXISTS schema_version (
  28. id INT PRIMARY KEY,
  29. version INT NOT NULL
  30. );
  31. COMMIT;
  32. `
  33. selectUserQuery = `SELECT pass, role FROM user WHERE user = ?`
  34. selectTopicPermsQuery = `
  35. SELECT read, write
  36. FROM access
  37. WHERE user IN ('*', ?) AND topic = ?
  38. ORDER BY user DESC
  39. `
  40. )
  41. // Manager-related queries
  42. const (
  43. insertUserQuery = `INSERT INTO user (user, pass, role) VALUES (?, ?, ?)`
  44. selectUsernamesQuery = `SELECT user FROM user ORDER BY role, user`
  45. updateUserPassQuery = `UPDATE user SET pass = ? WHERE user = ?`
  46. updateUserRoleQuery = `UPDATE user SET role = ? WHERE user = ?`
  47. deleteUserQuery = `DELETE FROM user WHERE user = ?`
  48. upsertUserAccessQuery = `
  49. INSERT INTO access (user, topic, read, write)
  50. VALUES (?, ?, ?, ?)
  51. ON CONFLICT (user, topic) DO UPDATE SET read=excluded.read, write=excluded.write
  52. `
  53. selectUserAccessQuery = `SELECT topic, read, write FROM access WHERE user = ?`
  54. deleteAllAccessQuery = `DELETE FROM access`
  55. deleteUserAccessQuery = `DELETE FROM access WHERE user = ?`
  56. deleteTopicAccessQuery = `DELETE FROM access WHERE user = ? AND topic = ?`
  57. )
  58. // SQLiteAuth is an implementation of Auther and Manager. It stores users and access control list
  59. // in a SQLite database.
  60. type SQLiteAuth struct {
  61. db *sql.DB
  62. defaultRead bool
  63. defaultWrite bool
  64. }
  65. var (
  66. allowedUsernameRegex = regexp.MustCompile(`^[-_.@a-zA-Z0-9]+$`)
  67. )
  68. var _ Auther = (*SQLiteAuth)(nil)
  69. var _ Manager = (*SQLiteAuth)(nil)
  70. // NewSQLiteAuth creates a new SQLiteAuth instance
  71. func NewSQLiteAuth(filename string, defaultRead, defaultWrite bool) (*SQLiteAuth, error) {
  72. db, err := sql.Open("sqlite3", filename)
  73. if err != nil {
  74. return nil, err
  75. }
  76. if err := setupNewAuthDB(db); err != nil {
  77. return nil, err
  78. }
  79. return &SQLiteAuth{
  80. db: db,
  81. defaultRead: defaultRead,
  82. defaultWrite: defaultWrite,
  83. }, nil
  84. }
  85. func setupNewAuthDB(db *sql.DB) error {
  86. if _, err := db.Exec(createAuthTablesQueries); err != nil {
  87. return err
  88. }
  89. // FIXME schema version
  90. return nil
  91. }
  92. // Authenticate checks username and password and returns a user if correct. The method
  93. // returns in constant-ish time, regardless of whether the user exists or the password is
  94. // correct or incorrect.
  95. func (a *SQLiteAuth) Authenticate(username, password string) (*User, error) {
  96. if username == Everyone {
  97. return nil, ErrUnauthenticated
  98. }
  99. user, err := a.User(username)
  100. if err != nil {
  101. bcrypt.CompareHashAndPassword([]byte("$2a$11$eX15DeF27FwAgXt9wqJF0uAUMz74XywJcGBH3kP93pzKYv6ATk2ka"),
  102. []byte("intentional slow-down to avoid timing attacks"))
  103. return nil, ErrUnauthenticated
  104. }
  105. if err := bcrypt.CompareHashAndPassword([]byte(user.Hash), []byte(password)); err != nil {
  106. return nil, ErrUnauthenticated
  107. }
  108. return user, nil
  109. }
  110. // Authorize returns nil if the given user has access to the given topic using the desired
  111. // permission. The user param may be nil to signal an anonymous user.
  112. func (a *SQLiteAuth) Authorize(user *User, topic string, perm Permission) error {
  113. if user != nil && user.Role == RoleAdmin {
  114. return nil // Admin can do everything
  115. }
  116. username := Everyone
  117. if user != nil {
  118. username = user.Name
  119. }
  120. // Select the read/write permissions for this user/topic combo. The query may return two
  121. // rows (one for everyone, and one for the user), but prioritizes the user. The value for
  122. // user.Name may be empty (= everyone).
  123. rows, err := a.db.Query(selectTopicPermsQuery, username, topic)
  124. if err != nil {
  125. return err
  126. }
  127. defer rows.Close()
  128. if !rows.Next() {
  129. return a.resolvePerms(a.defaultRead, a.defaultWrite, perm)
  130. }
  131. var read, write bool
  132. if err := rows.Scan(&read, &write); err != nil {
  133. return err
  134. } else if err := rows.Err(); err != nil {
  135. return err
  136. }
  137. return a.resolvePerms(read, write, perm)
  138. }
  139. func (a *SQLiteAuth) resolvePerms(read, write bool, perm Permission) error {
  140. if perm == PermissionRead && read {
  141. return nil
  142. } else if perm == PermissionWrite && write {
  143. return nil
  144. }
  145. return ErrUnauthorized
  146. }
  147. // AddUser adds a user with the given username, password and role. The password should be hashed
  148. // before it is stored in a persistence layer.
  149. func (a *SQLiteAuth) AddUser(username, password string, role Role) error {
  150. if !allowedUsernameRegex.MatchString(username) || !AllowedRole(role) {
  151. return ErrInvalidArgument
  152. }
  153. hash, err := bcrypt.GenerateFromPassword([]byte(password), bcryptCost)
  154. if err != nil {
  155. return err
  156. }
  157. if _, err = a.db.Exec(insertUserQuery, username, hash, role); err != nil {
  158. return err
  159. }
  160. return nil
  161. }
  162. // RemoveUser deletes the user with the given username. The function returns nil on success, even
  163. // if the user did not exist in the first place.
  164. func (a *SQLiteAuth) RemoveUser(username string) error {
  165. if !allowedUsernameRegex.MatchString(username) || username == Everyone {
  166. return ErrInvalidArgument
  167. }
  168. if _, err := a.db.Exec(deleteUserQuery, username); err != nil {
  169. return err
  170. }
  171. if _, err := a.db.Exec(deleteUserAccessQuery, username); err != nil {
  172. return err
  173. }
  174. return nil
  175. }
  176. // Users returns a list of users. It always also returns the Everyone user ("*").
  177. func (a *SQLiteAuth) Users() ([]*User, error) {
  178. rows, err := a.db.Query(selectUsernamesQuery)
  179. if err != nil {
  180. return nil, err
  181. }
  182. defer rows.Close()
  183. usernames := make([]string, 0)
  184. for rows.Next() {
  185. var username string
  186. if err := rows.Scan(&username); err != nil {
  187. return nil, err
  188. } else if err := rows.Err(); err != nil {
  189. return nil, err
  190. }
  191. usernames = append(usernames, username)
  192. }
  193. rows.Close()
  194. users := make([]*User, 0)
  195. for _, username := range usernames {
  196. user, err := a.User(username)
  197. if err != nil {
  198. return nil, err
  199. }
  200. users = append(users, user)
  201. }
  202. everyone, err := a.everyoneUser()
  203. if err != nil {
  204. return nil, err
  205. }
  206. users = append(users, everyone)
  207. return users, nil
  208. }
  209. // User returns the user with the given username if it exists, or ErrNotFound otherwise.
  210. // You may also pass Everyone to retrieve the anonymous user and its Grant list.
  211. func (a *SQLiteAuth) User(username string) (*User, error) {
  212. if username == Everyone {
  213. return a.everyoneUser()
  214. }
  215. rows, err := a.db.Query(selectUserQuery, username)
  216. if err != nil {
  217. return nil, err
  218. }
  219. defer rows.Close()
  220. var hash, role string
  221. if !rows.Next() {
  222. return nil, ErrNotFound
  223. }
  224. if err := rows.Scan(&hash, &role); err != nil {
  225. return nil, err
  226. } else if err := rows.Err(); err != nil {
  227. return nil, err
  228. }
  229. grants, err := a.readGrants(username)
  230. if err != nil {
  231. return nil, err
  232. }
  233. return &User{
  234. Name: username,
  235. Hash: hash,
  236. Role: Role(role),
  237. Grants: grants,
  238. }, nil
  239. }
  240. func (a *SQLiteAuth) everyoneUser() (*User, error) {
  241. grants, err := a.readGrants(Everyone)
  242. if err != nil {
  243. return nil, err
  244. }
  245. return &User{
  246. Name: Everyone,
  247. Hash: "",
  248. Role: RoleAnonymous,
  249. Grants: grants,
  250. }, nil
  251. }
  252. func (a *SQLiteAuth) readGrants(username string) ([]Grant, error) {
  253. rows, err := a.db.Query(selectUserAccessQuery, username)
  254. if err != nil {
  255. return nil, err
  256. }
  257. defer rows.Close()
  258. grants := make([]Grant, 0)
  259. for rows.Next() {
  260. var topic string
  261. var read, write bool
  262. if err := rows.Scan(&topic, &read, &write); err != nil {
  263. return nil, err
  264. } else if err := rows.Err(); err != nil {
  265. return nil, err
  266. }
  267. grants = append(grants, Grant{
  268. Topic: topic,
  269. Read: read,
  270. Write: write,
  271. })
  272. }
  273. return grants, nil
  274. }
  275. // ChangePassword changes a user's password
  276. func (a *SQLiteAuth) ChangePassword(username, password string) error {
  277. hash, err := bcrypt.GenerateFromPassword([]byte(password), bcryptCost)
  278. if err != nil {
  279. return err
  280. }
  281. if _, err := a.db.Exec(updateUserPassQuery, hash, username); err != nil {
  282. return err
  283. }
  284. return nil
  285. }
  286. // ChangeRole changes a user's role. When a role is changed from RoleUser to RoleAdmin,
  287. // all existing access control entries (Grant) are removed, since they are no longer needed.
  288. func (a *SQLiteAuth) ChangeRole(username string, role Role) error {
  289. if !allowedUsernameRegex.MatchString(username) || !AllowedRole(role) {
  290. return ErrInvalidArgument
  291. }
  292. if _, err := a.db.Exec(updateUserRoleQuery, string(role), username); err != nil {
  293. return err
  294. }
  295. if role == RoleAdmin {
  296. if _, err := a.db.Exec(deleteUserAccessQuery, username); err != nil {
  297. return err
  298. }
  299. }
  300. return nil
  301. }
  302. // AllowAccess adds or updates an entry in th access control list for a specific user. It controls
  303. // read/write access to a topic.
  304. func (a *SQLiteAuth) AllowAccess(username string, topic string, read bool, write bool) error {
  305. if _, err := a.db.Exec(upsertUserAccessQuery, username, topic, read, write); err != nil {
  306. return err
  307. }
  308. return nil
  309. }
  310. // ResetAccess removes an access control list entry for a specific username/topic, or (if topic is
  311. // empty) for an entire user.
  312. func (a *SQLiteAuth) ResetAccess(username string, topic string) error {
  313. if username == "" && topic == "" {
  314. _, err := a.db.Exec(deleteAllAccessQuery, username)
  315. return err
  316. } else if topic == "" {
  317. _, err := a.db.Exec(deleteUserAccessQuery, username)
  318. return err
  319. }
  320. _, err := a.db.Exec(deleteTopicAccessQuery, username, topic)
  321. return err
  322. }
  323. // DefaultAccess returns the default read/write access if no access control entry matches
  324. func (a *SQLiteAuth) DefaultAccess() (read bool, write bool) {
  325. return a.defaultRead, a.defaultWrite
  326. }