manager.go 54 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547
  1. // Package user deals with authentication and authorization against topics
  2. package user
  3. import (
  4. "database/sql"
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. _ "github.com/mattn/go-sqlite3" // SQLite driver
  9. "github.com/stripe/stripe-go/v74"
  10. "golang.org/x/crypto/bcrypt"
  11. "heckel.io/ntfy/log"
  12. "heckel.io/ntfy/util"
  13. "net/netip"
  14. "strings"
  15. "sync"
  16. "time"
  17. )
  18. const (
  19. tierIDPrefix = "ti_"
  20. tierIDLength = 8
  21. syncTopicPrefix = "st_"
  22. syncTopicLength = 16
  23. userIDPrefix = "u_"
  24. userIDLength = 12
  25. userAuthIntentionalSlowDownHash = "$2a$10$YFCQvqQDwIIwnJM1xkAYOeih0dg17UVGanaTStnrSzC8NCWxcLDwy" // Cost should match DefaultUserPasswordBcryptCost
  26. userHardDeleteAfterDuration = 7 * 24 * time.Hour
  27. tokenPrefix = "tk_"
  28. tokenLength = 32
  29. tokenMaxCount = 20 // Only keep this many tokens in the table per user
  30. tag = "user_manager"
  31. )
  32. // Default constants that may be overridden by configs
  33. const (
  34. DefaultUserStatsQueueWriterInterval = 33 * time.Second
  35. DefaultUserPasswordBcryptCost = 10
  36. )
  37. var (
  38. errNoTokenProvided = errors.New("no token provided")
  39. errTopicOwnedByOthers = errors.New("topic owned by others")
  40. errNoRows = errors.New("no rows found")
  41. )
  42. // Manager-related queries
  43. const (
  44. createTablesQueries = `
  45. BEGIN;
  46. CREATE TABLE IF NOT EXISTS tier (
  47. id TEXT PRIMARY KEY,
  48. code TEXT NOT NULL,
  49. name TEXT NOT NULL,
  50. messages_limit INT NOT NULL,
  51. messages_expiry_duration INT NOT NULL,
  52. emails_limit INT NOT NULL,
  53. sms_limit INT NOT NULL,
  54. calls_limit INT NOT NULL,
  55. reservations_limit INT NOT NULL,
  56. attachment_file_size_limit INT NOT NULL,
  57. attachment_total_size_limit INT NOT NULL,
  58. attachment_expiry_duration INT NOT NULL,
  59. attachment_bandwidth_limit INT NOT NULL,
  60. stripe_monthly_price_id TEXT,
  61. stripe_yearly_price_id TEXT
  62. );
  63. CREATE UNIQUE INDEX idx_tier_code ON tier (code);
  64. CREATE UNIQUE INDEX idx_tier_stripe_monthly_price_id ON tier (stripe_monthly_price_id);
  65. CREATE UNIQUE INDEX idx_tier_stripe_yearly_price_id ON tier (stripe_yearly_price_id);
  66. CREATE TABLE IF NOT EXISTS user (
  67. id TEXT PRIMARY KEY,
  68. tier_id TEXT,
  69. user TEXT NOT NULL,
  70. pass TEXT NOT NULL,
  71. role TEXT CHECK (role IN ('anonymous', 'admin', 'user')) NOT NULL,
  72. prefs JSON NOT NULL DEFAULT '{}',
  73. sync_topic TEXT NOT NULL,
  74. stats_messages INT NOT NULL DEFAULT (0),
  75. stats_emails INT NOT NULL DEFAULT (0),
  76. stats_sms INT NOT NULL DEFAULT (0),
  77. stats_calls INT NOT NULL DEFAULT (0),
  78. stripe_customer_id TEXT,
  79. stripe_subscription_id TEXT,
  80. stripe_subscription_status TEXT,
  81. stripe_subscription_interval TEXT,
  82. stripe_subscription_paid_until INT,
  83. stripe_subscription_cancel_at INT,
  84. created INT NOT NULL,
  85. deleted INT,
  86. FOREIGN KEY (tier_id) REFERENCES tier (id)
  87. );
  88. CREATE UNIQUE INDEX idx_user ON user (user);
  89. CREATE UNIQUE INDEX idx_user_stripe_customer_id ON user (stripe_customer_id);
  90. CREATE UNIQUE INDEX idx_user_stripe_subscription_id ON user (stripe_subscription_id);
  91. CREATE TABLE IF NOT EXISTS user_access (
  92. user_id TEXT NOT NULL,
  93. topic TEXT NOT NULL,
  94. read INT NOT NULL,
  95. write INT NOT NULL,
  96. owner_user_id INT,
  97. PRIMARY KEY (user_id, topic),
  98. FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE,
  99. FOREIGN KEY (owner_user_id) REFERENCES user (id) ON DELETE CASCADE
  100. );
  101. CREATE TABLE IF NOT EXISTS user_token (
  102. user_id TEXT NOT NULL,
  103. token TEXT NOT NULL,
  104. label TEXT NOT NULL,
  105. last_access INT NOT NULL,
  106. last_origin TEXT NOT NULL,
  107. expires INT NOT NULL,
  108. PRIMARY KEY (user_id, token),
  109. FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE
  110. );
  111. CREATE TABLE IF NOT EXISTS schemaVersion (
  112. id INT PRIMARY KEY,
  113. version INT NOT NULL
  114. );
  115. INSERT INTO user (id, user, pass, role, sync_topic, created)
  116. VALUES ('` + everyoneID + `', '*', '', 'anonymous', '', UNIXEPOCH())
  117. ON CONFLICT (id) DO NOTHING;
  118. COMMIT;
  119. `
  120. builtinStartupQueries = `
  121. PRAGMA foreign_keys = ON;
  122. `
  123. selectUserByIDQuery = `
  124. SELECT u.id, u.user, u.pass, u.role, u.prefs, u.sync_topic, u.stats_messages, u.stats_emails, u.stats_sms, u.stats_calls, u.stripe_customer_id, u.stripe_subscription_id, u.stripe_subscription_status, u.stripe_subscription_interval, u.stripe_subscription_paid_until, u.stripe_subscription_cancel_at, deleted, t.id, t.code, t.name, t.messages_limit, t.messages_expiry_duration, t.emails_limit, t.sms_limit, t.calls_limit, t.reservations_limit, t.attachment_file_size_limit, t.attachment_total_size_limit, t.attachment_expiry_duration, t.attachment_bandwidth_limit, t.stripe_monthly_price_id, t.stripe_yearly_price_id
  125. FROM user u
  126. LEFT JOIN tier t on t.id = u.tier_id
  127. WHERE u.id = ?
  128. `
  129. selectUserByNameQuery = `
  130. SELECT u.id, u.user, u.pass, u.role, u.prefs, u.sync_topic, u.stats_messages, u.stats_emails, u.stats_sms, u.stats_calls, u.stripe_customer_id, u.stripe_subscription_id, u.stripe_subscription_status, u.stripe_subscription_interval, u.stripe_subscription_paid_until, u.stripe_subscription_cancel_at, deleted, t.id, t.code, t.name, t.messages_limit, t.messages_expiry_duration, t.emails_limit, t.sms_limit, t.calls_limit, t.reservations_limit, t.attachment_file_size_limit, t.attachment_total_size_limit, t.attachment_expiry_duration, t.attachment_bandwidth_limit, t.stripe_monthly_price_id, t.stripe_yearly_price_id
  131. FROM user u
  132. LEFT JOIN tier t on t.id = u.tier_id
  133. WHERE user = ?
  134. `
  135. selectUserByTokenQuery = `
  136. SELECT u.id, u.user, u.pass, u.role, u.prefs, u.sync_topic, u.stats_messages, u.stats_emails, u.stats_sms, u.stats_calls, u.stripe_customer_id, u.stripe_subscription_id, u.stripe_subscription_status, u.stripe_subscription_interval, u.stripe_subscription_paid_until, u.stripe_subscription_cancel_at, deleted, t.id, t.code, t.name, t.messages_limit, t.messages_expiry_duration, t.emails_limit, t.sms_limit, t.calls_limit, t.reservations_limit, t.attachment_file_size_limit, t.attachment_total_size_limit, t.attachment_expiry_duration, t.attachment_bandwidth_limit, t.stripe_monthly_price_id, t.stripe_yearly_price_id
  137. FROM user u
  138. JOIN user_token tk on u.id = tk.user_id
  139. LEFT JOIN tier t on t.id = u.tier_id
  140. WHERE tk.token = ? AND (tk.expires = 0 OR tk.expires >= ?)
  141. `
  142. selectUserByStripeCustomerIDQuery = `
  143. SELECT u.id, u.user, u.pass, u.role, u.prefs, u.sync_topic, u.stats_messages, u.stats_emails, u.stats_sms, u.stats_calls, u.stripe_customer_id, u.stripe_subscription_id, u.stripe_subscription_status, u.stripe_subscription_interval, u.stripe_subscription_paid_until, u.stripe_subscription_cancel_at, deleted, t.id, t.code, t.name, t.messages_limit, t.messages_expiry_duration, t.emails_limit, t.sms_limit, t.calls_limit, t.reservations_limit, t.attachment_file_size_limit, t.attachment_total_size_limit, t.attachment_expiry_duration, t.attachment_bandwidth_limit, t.stripe_monthly_price_id, t.stripe_yearly_price_id
  144. FROM user u
  145. LEFT JOIN tier t on t.id = u.tier_id
  146. WHERE u.stripe_customer_id = ?
  147. `
  148. selectTopicPermsQuery = `
  149. SELECT read, write
  150. FROM user_access a
  151. JOIN user u ON u.id = a.user_id
  152. WHERE (u.user = ? OR u.user = ?) AND ? LIKE a.topic
  153. ORDER BY u.user DESC
  154. `
  155. insertUserQuery = `
  156. INSERT INTO user (id, user, pass, role, sync_topic, created)
  157. VALUES (?, ?, ?, ?, ?, ?)
  158. `
  159. selectUsernamesQuery = `
  160. SELECT user
  161. FROM user
  162. ORDER BY
  163. CASE role
  164. WHEN 'admin' THEN 1
  165. WHEN 'anonymous' THEN 3
  166. ELSE 2
  167. END, user
  168. `
  169. selectUserCountQuery = `SELECT COUNT(*) FROM user`
  170. updateUserPassQuery = `UPDATE user SET pass = ? WHERE user = ?`
  171. updateUserRoleQuery = `UPDATE user SET role = ? WHERE user = ?`
  172. updateUserPrefsQuery = `UPDATE user SET prefs = ? WHERE id = ?`
  173. updateUserStatsQuery = `UPDATE user SET stats_messages = ?, stats_emails = ?, stats_sms = ?, stats_calls = ? WHERE id = ?`
  174. updateUserStatsResetAllQuery = `UPDATE user SET stats_messages = 0, stats_emails = 0, stats_sms = 0, stats_calls = 0`
  175. updateUserDeletedQuery = `UPDATE user SET deleted = ? WHERE id = ?`
  176. deleteUsersMarkedQuery = `DELETE FROM user WHERE deleted < ?`
  177. deleteUserQuery = `DELETE FROM user WHERE user = ?`
  178. upsertUserAccessQuery = `
  179. INSERT INTO user_access (user_id, topic, read, write, owner_user_id)
  180. VALUES ((SELECT id FROM user WHERE user = ?), ?, ?, ?, (SELECT IIF(?='',NULL,(SELECT id FROM user WHERE user=?))))
  181. ON CONFLICT (user_id, topic)
  182. DO UPDATE SET read=excluded.read, write=excluded.write, owner_user_id=excluded.owner_user_id
  183. `
  184. selectUserAccessQuery = `
  185. SELECT topic, read, write
  186. FROM user_access
  187. WHERE user_id = (SELECT id FROM user WHERE user = ?)
  188. ORDER BY write DESC, read DESC, topic
  189. `
  190. selectUserReservationsQuery = `
  191. SELECT a_user.topic, a_user.read, a_user.write, a_everyone.read AS everyone_read, a_everyone.write AS everyone_write
  192. FROM user_access a_user
  193. LEFT JOIN user_access a_everyone ON a_user.topic = a_everyone.topic AND a_everyone.user_id = (SELECT id FROM user WHERE user = ?)
  194. WHERE a_user.user_id = a_user.owner_user_id
  195. AND a_user.owner_user_id = (SELECT id FROM user WHERE user = ?)
  196. ORDER BY a_user.topic
  197. `
  198. selectUserReservationsCountQuery = `
  199. SELECT COUNT(*)
  200. FROM user_access
  201. WHERE user_id = owner_user_id
  202. AND owner_user_id = (SELECT id FROM user WHERE user = ?)
  203. `
  204. selectUserReservationsOwnerQuery = `
  205. SELECT owner_user_id
  206. FROM user_access
  207. WHERE topic = ?
  208. AND user_id = owner_user_id
  209. `
  210. selectUserHasReservationQuery = `
  211. SELECT COUNT(*)
  212. FROM user_access
  213. WHERE user_id = owner_user_id
  214. AND owner_user_id = (SELECT id FROM user WHERE user = ?)
  215. AND topic = ?
  216. `
  217. selectOtherAccessCountQuery = `
  218. SELECT COUNT(*)
  219. FROM user_access
  220. WHERE (topic = ? OR ? LIKE topic)
  221. AND (owner_user_id IS NULL OR owner_user_id != (SELECT id FROM user WHERE user = ?))
  222. `
  223. deleteAllAccessQuery = `DELETE FROM user_access`
  224. deleteUserAccessQuery = `
  225. DELETE FROM user_access
  226. WHERE user_id = (SELECT id FROM user WHERE user = ?)
  227. OR owner_user_id = (SELECT id FROM user WHERE user = ?)
  228. `
  229. deleteTopicAccessQuery = `
  230. DELETE FROM user_access
  231. WHERE (user_id = (SELECT id FROM user WHERE user = ?) OR owner_user_id = (SELECT id FROM user WHERE user = ?))
  232. AND topic = ?
  233. `
  234. selectTokenCountQuery = `SELECT COUNT(*) FROM user_token WHERE user_id = ?`
  235. selectTokensQuery = `SELECT token, label, last_access, last_origin, expires FROM user_token WHERE user_id = ?`
  236. selectTokenQuery = `SELECT token, label, last_access, last_origin, expires FROM user_token WHERE user_id = ? AND token = ?`
  237. insertTokenQuery = `INSERT INTO user_token (user_id, token, label, last_access, last_origin, expires) VALUES (?, ?, ?, ?, ?, ?)`
  238. updateTokenExpiryQuery = `UPDATE user_token SET expires = ? WHERE user_id = ? AND token = ?`
  239. updateTokenLabelQuery = `UPDATE user_token SET label = ? WHERE user_id = ? AND token = ?`
  240. updateTokenLastAccessQuery = `UPDATE user_token SET last_access = ?, last_origin = ? WHERE token = ?`
  241. deleteTokenQuery = `DELETE FROM user_token WHERE user_id = ? AND token = ?`
  242. deleteAllTokenQuery = `DELETE FROM user_token WHERE user_id = ?`
  243. deleteExpiredTokensQuery = `DELETE FROM user_token WHERE expires > 0 AND expires < ?`
  244. deleteExcessTokensQuery = `
  245. DELETE FROM user_token
  246. WHERE (user_id, token) NOT IN (
  247. SELECT user_id, token
  248. FROM user_token
  249. WHERE user_id = ?
  250. ORDER BY expires DESC
  251. LIMIT ?
  252. )
  253. `
  254. insertTierQuery = `
  255. INSERT INTO tier (id, code, name, messages_limit, messages_expiry_duration, emails_limit, sms_limit, calls_limit, reservations_limit, attachment_file_size_limit, attachment_total_size_limit, attachment_expiry_duration, attachment_bandwidth_limit, stripe_monthly_price_id, stripe_yearly_price_id)
  256. VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
  257. `
  258. updateTierQuery = `
  259. UPDATE tier
  260. SET name = ?, messages_limit = ?, messages_expiry_duration = ?, emails_limit = ?, sms_limit = ?, calls_limit = ?, reservations_limit = ?, attachment_file_size_limit = ?, attachment_total_size_limit = ?, attachment_expiry_duration = ?, attachment_bandwidth_limit = ?, stripe_monthly_price_id = ?, stripe_yearly_price_id = ?
  261. WHERE code = ?
  262. `
  263. selectTiersQuery = `
  264. SELECT id, code, name, messages_limit, messages_expiry_duration, emails_limit, sms_limit, calls_limit, reservations_limit, attachment_file_size_limit, attachment_total_size_limit, attachment_expiry_duration, attachment_bandwidth_limit, stripe_monthly_price_id, stripe_yearly_price_id
  265. FROM tier
  266. `
  267. selectTierByCodeQuery = `
  268. SELECT id, code, name, messages_limit, messages_expiry_duration, emails_limit, sms_limit, calls_limit, reservations_limit, attachment_file_size_limit, attachment_total_size_limit, attachment_expiry_duration, attachment_bandwidth_limit, stripe_monthly_price_id, stripe_yearly_price_id
  269. FROM tier
  270. WHERE code = ?
  271. `
  272. selectTierByPriceIDQuery = `
  273. SELECT id, code, name, messages_limit, messages_expiry_duration, emails_limit, sms_limit, calls_limit, reservations_limit, attachment_file_size_limit, attachment_total_size_limit, attachment_expiry_duration, attachment_bandwidth_limit, stripe_monthly_price_id, stripe_yearly_price_id
  274. FROM tier
  275. WHERE (stripe_monthly_price_id = ? OR stripe_yearly_price_id = ?)
  276. `
  277. updateUserTierQuery = `UPDATE user SET tier_id = (SELECT id FROM tier WHERE code = ?) WHERE user = ?`
  278. deleteUserTierQuery = `UPDATE user SET tier_id = null WHERE user = ?`
  279. deleteTierQuery = `DELETE FROM tier WHERE code = ?`
  280. updateBillingQuery = `
  281. UPDATE user
  282. SET stripe_customer_id = ?, stripe_subscription_id = ?, stripe_subscription_status = ?, stripe_subscription_interval = ?, stripe_subscription_paid_until = ?, stripe_subscription_cancel_at = ?
  283. WHERE user = ?
  284. `
  285. )
  286. // Schema management queries
  287. const (
  288. currentSchemaVersion = 4
  289. insertSchemaVersion = `INSERT INTO schemaVersion VALUES (1, ?)`
  290. updateSchemaVersion = `UPDATE schemaVersion SET version = ? WHERE id = 1`
  291. selectSchemaVersionQuery = `SELECT version FROM schemaVersion WHERE id = 1`
  292. // 1 -> 2 (complex migration!)
  293. migrate1To2CreateTablesQueries = `
  294. ALTER TABLE user RENAME TO user_old;
  295. CREATE TABLE IF NOT EXISTS tier (
  296. id TEXT PRIMARY KEY,
  297. code TEXT NOT NULL,
  298. name TEXT NOT NULL,
  299. messages_limit INT NOT NULL,
  300. messages_expiry_duration INT NOT NULL,
  301. emails_limit INT NOT NULL,
  302. reservations_limit INT NOT NULL,
  303. attachment_file_size_limit INT NOT NULL,
  304. attachment_total_size_limit INT NOT NULL,
  305. attachment_expiry_duration INT NOT NULL,
  306. attachment_bandwidth_limit INT NOT NULL,
  307. stripe_price_id TEXT
  308. );
  309. CREATE UNIQUE INDEX idx_tier_code ON tier (code);
  310. CREATE UNIQUE INDEX idx_tier_price_id ON tier (stripe_price_id);
  311. CREATE TABLE IF NOT EXISTS user (
  312. id TEXT PRIMARY KEY,
  313. tier_id TEXT,
  314. user TEXT NOT NULL,
  315. pass TEXT NOT NULL,
  316. role TEXT CHECK (role IN ('anonymous', 'admin', 'user')) NOT NULL,
  317. prefs JSON NOT NULL DEFAULT '{}',
  318. sync_topic TEXT NOT NULL,
  319. stats_messages INT NOT NULL DEFAULT (0),
  320. stats_emails INT NOT NULL DEFAULT (0),
  321. stripe_customer_id TEXT,
  322. stripe_subscription_id TEXT,
  323. stripe_subscription_status TEXT,
  324. stripe_subscription_paid_until INT,
  325. stripe_subscription_cancel_at INT,
  326. created INT NOT NULL,
  327. deleted INT,
  328. FOREIGN KEY (tier_id) REFERENCES tier (id)
  329. );
  330. CREATE UNIQUE INDEX idx_user ON user (user);
  331. CREATE UNIQUE INDEX idx_user_stripe_customer_id ON user (stripe_customer_id);
  332. CREATE UNIQUE INDEX idx_user_stripe_subscription_id ON user (stripe_subscription_id);
  333. CREATE TABLE IF NOT EXISTS user_access (
  334. user_id TEXT NOT NULL,
  335. topic TEXT NOT NULL,
  336. read INT NOT NULL,
  337. write INT NOT NULL,
  338. owner_user_id INT,
  339. PRIMARY KEY (user_id, topic),
  340. FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE,
  341. FOREIGN KEY (owner_user_id) REFERENCES user (id) ON DELETE CASCADE
  342. );
  343. CREATE TABLE IF NOT EXISTS user_token (
  344. user_id TEXT NOT NULL,
  345. token TEXT NOT NULL,
  346. label TEXT NOT NULL,
  347. last_access INT NOT NULL,
  348. last_origin TEXT NOT NULL,
  349. expires INT NOT NULL,
  350. PRIMARY KEY (user_id, token),
  351. FOREIGN KEY (user_id) REFERENCES user (id) ON DELETE CASCADE
  352. );
  353. CREATE TABLE IF NOT EXISTS schemaVersion (
  354. id INT PRIMARY KEY,
  355. version INT NOT NULL
  356. );
  357. INSERT INTO user (id, user, pass, role, sync_topic, created)
  358. VALUES ('u_everyone', '*', '', 'anonymous', '', UNIXEPOCH())
  359. ON CONFLICT (id) DO NOTHING;
  360. `
  361. migrate1To2SelectAllOldUsernamesNoTx = `SELECT user FROM user_old`
  362. migrate1To2InsertUserNoTx = `
  363. INSERT INTO user (id, user, pass, role, sync_topic, created)
  364. SELECT ?, user, pass, role, ?, UNIXEPOCH() FROM user_old WHERE user = ?
  365. `
  366. migrate1To2InsertFromOldTablesAndDropNoTx = `
  367. INSERT INTO user_access (user_id, topic, read, write)
  368. SELECT u.id, a.topic, a.read, a.write
  369. FROM user u
  370. JOIN access a ON u.user = a.user;
  371. DROP TABLE access;
  372. DROP TABLE user_old;
  373. `
  374. // 2 -> 3
  375. migrate2To3UpdateQueries = `
  376. ALTER TABLE user ADD COLUMN stripe_subscription_interval TEXT;
  377. ALTER TABLE tier RENAME COLUMN stripe_price_id TO stripe_monthly_price_id;
  378. ALTER TABLE tier ADD COLUMN stripe_yearly_price_id TEXT;
  379. DROP INDEX IF EXISTS idx_tier_price_id;
  380. CREATE UNIQUE INDEX idx_tier_stripe_monthly_price_id ON tier (stripe_monthly_price_id);
  381. CREATE UNIQUE INDEX idx_tier_stripe_yearly_price_id ON tier (stripe_yearly_price_id);
  382. `
  383. // 3 -> 4
  384. migrate3To4UpdateQueries = `
  385. ALTER TABLE tier ADD COLUMN sms_limit INT NOT NULL DEFAULT (0);
  386. ALTER TABLE tier ADD COLUMN calls_limit INT NOT NULL DEFAULT (0);
  387. ALTER TABLE user ADD COLUMN stats_sms INT NOT NULL DEFAULT (0);
  388. ALTER TABLE user ADD COLUMN stats_calls INT NOT NULL DEFAULT (0);
  389. `
  390. )
  391. var (
  392. migrations = map[int]func(db *sql.DB) error{
  393. 1: migrateFrom1,
  394. 2: migrateFrom2,
  395. 3: migrateFrom3,
  396. }
  397. )
  398. // Manager is an implementation of Manager. It stores users and access control list
  399. // in a SQLite database.
  400. type Manager struct {
  401. db *sql.DB
  402. defaultAccess Permission // Default permission if no ACL matches
  403. statsQueue map[string]*Stats // "Queue" to asynchronously write user stats to the database (UserID -> Stats)
  404. tokenQueue map[string]*TokenUpdate // "Queue" to asynchronously write token access stats to the database (Token ID -> TokenUpdate)
  405. bcryptCost int // Makes testing easier
  406. mu sync.Mutex
  407. }
  408. var _ Auther = (*Manager)(nil)
  409. // NewManager creates a new Manager instance
  410. func NewManager(filename, startupQueries string, defaultAccess Permission, bcryptCost int, queueWriterInterval time.Duration) (*Manager, error) {
  411. db, err := sql.Open("sqlite3", filename)
  412. if err != nil {
  413. return nil, err
  414. }
  415. if err := setupDB(db); err != nil {
  416. return nil, err
  417. }
  418. if err := runStartupQueries(db, startupQueries); err != nil {
  419. return nil, err
  420. }
  421. manager := &Manager{
  422. db: db,
  423. defaultAccess: defaultAccess,
  424. statsQueue: make(map[string]*Stats),
  425. tokenQueue: make(map[string]*TokenUpdate),
  426. bcryptCost: bcryptCost,
  427. }
  428. go manager.asyncQueueWriter(queueWriterInterval)
  429. return manager, nil
  430. }
  431. // Authenticate checks username and password and returns a User if correct, and the user has not been
  432. // marked as deleted. The method returns in constant-ish time, regardless of whether the user exists or
  433. // the password is correct or incorrect.
  434. func (a *Manager) Authenticate(username, password string) (*User, error) {
  435. if username == Everyone {
  436. return nil, ErrUnauthenticated
  437. }
  438. user, err := a.User(username)
  439. if err != nil {
  440. log.Tag(tag).Field("user_name", username).Err(err).Trace("Authentication of user failed (1)")
  441. bcrypt.CompareHashAndPassword([]byte(userAuthIntentionalSlowDownHash), []byte("intentional slow-down to avoid timing attacks"))
  442. return nil, ErrUnauthenticated
  443. } else if user.Deleted {
  444. log.Tag(tag).Field("user_name", username).Trace("Authentication of user failed (2): user marked deleted")
  445. bcrypt.CompareHashAndPassword([]byte(userAuthIntentionalSlowDownHash), []byte("intentional slow-down to avoid timing attacks"))
  446. return nil, ErrUnauthenticated
  447. } else if err := bcrypt.CompareHashAndPassword([]byte(user.Hash), []byte(password)); err != nil {
  448. log.Tag(tag).Field("user_name", username).Err(err).Trace("Authentication of user failed (3)")
  449. return nil, ErrUnauthenticated
  450. }
  451. return user, nil
  452. }
  453. // AuthenticateToken checks if the token exists and returns the associated User if it does.
  454. // The method sets the User.Token value to the token that was used for authentication.
  455. func (a *Manager) AuthenticateToken(token string) (*User, error) {
  456. if len(token) != tokenLength {
  457. return nil, ErrUnauthenticated
  458. }
  459. user, err := a.userByToken(token)
  460. if err != nil {
  461. log.Tag(tag).Field("token", token).Err(err).Trace("Authentication of token failed")
  462. return nil, ErrUnauthenticated
  463. }
  464. user.Token = token
  465. return user, nil
  466. }
  467. // CreateToken generates a random token for the given user and returns it. The token expires
  468. // after a fixed duration unless ChangeToken is called. This function also prunes tokens for the
  469. // given user, if there are too many of them.
  470. func (a *Manager) CreateToken(userID, label string, expires time.Time, origin netip.Addr) (*Token, error) {
  471. token := util.RandomStringPrefix(tokenPrefix, tokenLength)
  472. tx, err := a.db.Begin()
  473. if err != nil {
  474. return nil, err
  475. }
  476. defer tx.Rollback()
  477. access := time.Now()
  478. if _, err := tx.Exec(insertTokenQuery, userID, token, label, access.Unix(), origin.String(), expires.Unix()); err != nil {
  479. return nil, err
  480. }
  481. rows, err := tx.Query(selectTokenCountQuery, userID)
  482. if err != nil {
  483. return nil, err
  484. }
  485. defer rows.Close()
  486. if !rows.Next() {
  487. return nil, errNoRows
  488. }
  489. var tokenCount int
  490. if err := rows.Scan(&tokenCount); err != nil {
  491. return nil, err
  492. }
  493. if tokenCount >= tokenMaxCount {
  494. // This pruning logic is done in two queries for efficiency. The SELECT above is a lookup
  495. // on two indices, whereas the query below is a full table scan.
  496. if _, err := tx.Exec(deleteExcessTokensQuery, userID, tokenMaxCount); err != nil {
  497. return nil, err
  498. }
  499. }
  500. if err := tx.Commit(); err != nil {
  501. return nil, err
  502. }
  503. return &Token{
  504. Value: token,
  505. Label: label,
  506. LastAccess: access,
  507. LastOrigin: origin,
  508. Expires: expires,
  509. }, nil
  510. }
  511. // Tokens returns all existing tokens for the user with the given user ID
  512. func (a *Manager) Tokens(userID string) ([]*Token, error) {
  513. rows, err := a.db.Query(selectTokensQuery, userID)
  514. if err != nil {
  515. return nil, err
  516. }
  517. defer rows.Close()
  518. tokens := make([]*Token, 0)
  519. for {
  520. token, err := a.readToken(rows)
  521. if err == ErrTokenNotFound {
  522. break
  523. } else if err != nil {
  524. return nil, err
  525. }
  526. tokens = append(tokens, token)
  527. }
  528. return tokens, nil
  529. }
  530. // Token returns a specific token for a user
  531. func (a *Manager) Token(userID, token string) (*Token, error) {
  532. rows, err := a.db.Query(selectTokenQuery, userID, token)
  533. if err != nil {
  534. return nil, err
  535. }
  536. defer rows.Close()
  537. return a.readToken(rows)
  538. }
  539. func (a *Manager) readToken(rows *sql.Rows) (*Token, error) {
  540. var token, label, lastOrigin string
  541. var lastAccess, expires int64
  542. if !rows.Next() {
  543. return nil, ErrTokenNotFound
  544. }
  545. if err := rows.Scan(&token, &label, &lastAccess, &lastOrigin, &expires); err != nil {
  546. return nil, err
  547. } else if err := rows.Err(); err != nil {
  548. return nil, err
  549. }
  550. lastOriginIP, err := netip.ParseAddr(lastOrigin)
  551. if err != nil {
  552. lastOriginIP = netip.IPv4Unspecified()
  553. }
  554. return &Token{
  555. Value: token,
  556. Label: label,
  557. LastAccess: time.Unix(lastAccess, 0),
  558. LastOrigin: lastOriginIP,
  559. Expires: time.Unix(expires, 0),
  560. }, nil
  561. }
  562. // ChangeToken updates a token's label and/or expiry date
  563. func (a *Manager) ChangeToken(userID, token string, label *string, expires *time.Time) (*Token, error) {
  564. if token == "" {
  565. return nil, errNoTokenProvided
  566. }
  567. tx, err := a.db.Begin()
  568. if err != nil {
  569. return nil, err
  570. }
  571. defer tx.Rollback()
  572. if label != nil {
  573. if _, err := tx.Exec(updateTokenLabelQuery, *label, userID, token); err != nil {
  574. return nil, err
  575. }
  576. }
  577. if expires != nil {
  578. if _, err := tx.Exec(updateTokenExpiryQuery, expires.Unix(), userID, token); err != nil {
  579. return nil, err
  580. }
  581. }
  582. if err := tx.Commit(); err != nil {
  583. return nil, err
  584. }
  585. return a.Token(userID, token)
  586. }
  587. // RemoveToken deletes the token defined in User.Token
  588. func (a *Manager) RemoveToken(userID, token string) error {
  589. if token == "" {
  590. return errNoTokenProvided
  591. }
  592. if _, err := a.db.Exec(deleteTokenQuery, userID, token); err != nil {
  593. return err
  594. }
  595. return nil
  596. }
  597. // RemoveExpiredTokens deletes all expired tokens from the database
  598. func (a *Manager) RemoveExpiredTokens() error {
  599. if _, err := a.db.Exec(deleteExpiredTokensQuery, time.Now().Unix()); err != nil {
  600. return err
  601. }
  602. return nil
  603. }
  604. // RemoveDeletedUsers deletes all users that have been marked deleted for
  605. func (a *Manager) RemoveDeletedUsers() error {
  606. if _, err := a.db.Exec(deleteUsersMarkedQuery, time.Now().Unix()); err != nil {
  607. return err
  608. }
  609. return nil
  610. }
  611. // ChangeSettings persists the user settings
  612. func (a *Manager) ChangeSettings(userID string, prefs *Prefs) error {
  613. b, err := json.Marshal(prefs)
  614. if err != nil {
  615. return err
  616. }
  617. if _, err := a.db.Exec(updateUserPrefsQuery, string(b), userID); err != nil {
  618. return err
  619. }
  620. return nil
  621. }
  622. // ResetStats resets all user stats in the user database. This touches all users.
  623. func (a *Manager) ResetStats() error {
  624. a.mu.Lock() // Includes database query to avoid races!
  625. defer a.mu.Unlock()
  626. if _, err := a.db.Exec(updateUserStatsResetAllQuery); err != nil {
  627. return err
  628. }
  629. a.statsQueue = make(map[string]*Stats)
  630. return nil
  631. }
  632. // EnqueueUserStats adds the user to a queue which writes out user stats (messages, emails, ..) in
  633. // batches at a regular interval
  634. func (a *Manager) EnqueueUserStats(userID string, stats *Stats) {
  635. a.mu.Lock()
  636. defer a.mu.Unlock()
  637. a.statsQueue[userID] = stats
  638. }
  639. // EnqueueTokenUpdate adds the token update to a queue which writes out token access times
  640. // in batches at a regular interval
  641. func (a *Manager) EnqueueTokenUpdate(tokenID string, update *TokenUpdate) {
  642. a.mu.Lock()
  643. defer a.mu.Unlock()
  644. a.tokenQueue[tokenID] = update
  645. }
  646. func (a *Manager) asyncQueueWriter(interval time.Duration) {
  647. ticker := time.NewTicker(interval)
  648. for range ticker.C {
  649. if err := a.writeUserStatsQueue(); err != nil {
  650. log.Tag(tag).Err(err).Warn("Writing user stats queue failed")
  651. }
  652. if err := a.writeTokenUpdateQueue(); err != nil {
  653. log.Tag(tag).Err(err).Warn("Writing token update queue failed")
  654. }
  655. }
  656. }
  657. func (a *Manager) writeUserStatsQueue() error {
  658. a.mu.Lock()
  659. if len(a.statsQueue) == 0 {
  660. a.mu.Unlock()
  661. log.Tag(tag).Trace("No user stats updates to commit")
  662. return nil
  663. }
  664. statsQueue := a.statsQueue
  665. a.statsQueue = make(map[string]*Stats)
  666. a.mu.Unlock()
  667. tx, err := a.db.Begin()
  668. if err != nil {
  669. return err
  670. }
  671. defer tx.Rollback()
  672. log.Tag(tag).Debug("Writing user stats queue for %d user(s)", len(statsQueue))
  673. for userID, update := range statsQueue {
  674. log.
  675. Tag(tag).
  676. Fields(log.Context{
  677. "user_id": userID,
  678. "messages_count": update.Messages,
  679. "emails_count": update.Emails,
  680. "sms_count": update.SMS,
  681. "calls_count": update.Calls,
  682. }).
  683. Trace("Updating stats for user %s", userID)
  684. if _, err := tx.Exec(updateUserStatsQuery, update.Messages, update.Emails, update.SMS, update.Calls, userID); err != nil {
  685. return err
  686. }
  687. }
  688. return tx.Commit()
  689. }
  690. func (a *Manager) writeTokenUpdateQueue() error {
  691. a.mu.Lock()
  692. if len(a.tokenQueue) == 0 {
  693. a.mu.Unlock()
  694. log.Tag(tag).Trace("No token updates to commit")
  695. return nil
  696. }
  697. tokenQueue := a.tokenQueue
  698. a.tokenQueue = make(map[string]*TokenUpdate)
  699. a.mu.Unlock()
  700. tx, err := a.db.Begin()
  701. if err != nil {
  702. return err
  703. }
  704. defer tx.Rollback()
  705. log.Tag(tag).Debug("Writing token update queue for %d token(s)", len(tokenQueue))
  706. for tokenID, update := range tokenQueue {
  707. log.Tag(tag).Trace("Updating token %s with last access time %v", tokenID, update.LastAccess.Unix())
  708. if _, err := tx.Exec(updateTokenLastAccessQuery, update.LastAccess.Unix(), update.LastOrigin.String(), tokenID); err != nil {
  709. return err
  710. }
  711. }
  712. return tx.Commit()
  713. }
  714. // Authorize returns nil if the given user has access to the given topic using the desired
  715. // permission. The user param may be nil to signal an anonymous user.
  716. func (a *Manager) Authorize(user *User, topic string, perm Permission) error {
  717. if user != nil && user.Role == RoleAdmin {
  718. return nil // Admin can do everything
  719. }
  720. username := Everyone
  721. if user != nil {
  722. username = user.Name
  723. }
  724. // Select the read/write permissions for this user/topic combo. The query may return two
  725. // rows (one for everyone, and one for the user), but prioritizes the user.
  726. rows, err := a.db.Query(selectTopicPermsQuery, Everyone, username, topic)
  727. if err != nil {
  728. return err
  729. }
  730. defer rows.Close()
  731. if !rows.Next() {
  732. return a.resolvePerms(a.defaultAccess, perm)
  733. }
  734. var read, write bool
  735. if err := rows.Scan(&read, &write); err != nil {
  736. return err
  737. } else if err := rows.Err(); err != nil {
  738. return err
  739. }
  740. return a.resolvePerms(NewPermission(read, write), perm)
  741. }
  742. func (a *Manager) resolvePerms(base, perm Permission) error {
  743. if perm == PermissionRead && base.IsRead() {
  744. return nil
  745. } else if perm == PermissionWrite && base.IsWrite() {
  746. return nil
  747. }
  748. return ErrUnauthorized
  749. }
  750. // AddUser adds a user with the given username, password and role
  751. func (a *Manager) AddUser(username, password string, role Role) error {
  752. if !AllowedUsername(username) || !AllowedRole(role) {
  753. return ErrInvalidArgument
  754. }
  755. hash, err := bcrypt.GenerateFromPassword([]byte(password), a.bcryptCost)
  756. if err != nil {
  757. return err
  758. }
  759. userID := util.RandomStringPrefix(userIDPrefix, userIDLength)
  760. syncTopic, now := util.RandomStringPrefix(syncTopicPrefix, syncTopicLength), time.Now().Unix()
  761. if _, err = a.db.Exec(insertUserQuery, userID, username, hash, role, syncTopic, now); err != nil {
  762. return err
  763. }
  764. return nil
  765. }
  766. // RemoveUser deletes the user with the given username. The function returns nil on success, even
  767. // if the user did not exist in the first place.
  768. func (a *Manager) RemoveUser(username string) error {
  769. if !AllowedUsername(username) {
  770. return ErrInvalidArgument
  771. }
  772. // Rows in user_access, user_token, etc. are deleted via foreign keys
  773. if _, err := a.db.Exec(deleteUserQuery, username); err != nil {
  774. return err
  775. }
  776. return nil
  777. }
  778. // MarkUserRemoved sets the deleted flag on the user, and deletes all access tokens. This prevents
  779. // successful auth via Authenticate. A background process will delete the user at a later date.
  780. func (a *Manager) MarkUserRemoved(user *User) error {
  781. if !AllowedUsername(user.Name) {
  782. return ErrInvalidArgument
  783. }
  784. tx, err := a.db.Begin()
  785. if err != nil {
  786. return err
  787. }
  788. defer tx.Rollback()
  789. if _, err := tx.Exec(deleteUserAccessQuery, user.Name, user.Name); err != nil {
  790. return err
  791. }
  792. if _, err := tx.Exec(deleteAllTokenQuery, user.ID); err != nil {
  793. return err
  794. }
  795. if _, err := tx.Exec(updateUserDeletedQuery, time.Now().Add(userHardDeleteAfterDuration).Unix(), user.ID); err != nil {
  796. return err
  797. }
  798. return tx.Commit()
  799. }
  800. // Users returns a list of users. It always also returns the Everyone user ("*").
  801. func (a *Manager) Users() ([]*User, error) {
  802. rows, err := a.db.Query(selectUsernamesQuery)
  803. if err != nil {
  804. return nil, err
  805. }
  806. defer rows.Close()
  807. usernames := make([]string, 0)
  808. for rows.Next() {
  809. var username string
  810. if err := rows.Scan(&username); err != nil {
  811. return nil, err
  812. } else if err := rows.Err(); err != nil {
  813. return nil, err
  814. }
  815. usernames = append(usernames, username)
  816. }
  817. rows.Close()
  818. users := make([]*User, 0)
  819. for _, username := range usernames {
  820. user, err := a.User(username)
  821. if err != nil {
  822. return nil, err
  823. }
  824. users = append(users, user)
  825. }
  826. return users, nil
  827. }
  828. // UsersCount returns the number of users in the databsae
  829. func (a *Manager) UsersCount() (int64, error) {
  830. rows, err := a.db.Query(selectUserCountQuery)
  831. if err != nil {
  832. return 0, err
  833. }
  834. defer rows.Close()
  835. if !rows.Next() {
  836. return 0, errNoRows
  837. }
  838. var count int64
  839. if err := rows.Scan(&count); err != nil {
  840. return 0, err
  841. }
  842. return count, nil
  843. }
  844. // User returns the user with the given username if it exists, or ErrUserNotFound otherwise.
  845. // You may also pass Everyone to retrieve the anonymous user and its Grant list.
  846. func (a *Manager) User(username string) (*User, error) {
  847. rows, err := a.db.Query(selectUserByNameQuery, username)
  848. if err != nil {
  849. return nil, err
  850. }
  851. return a.readUser(rows)
  852. }
  853. // UserByID returns the user with the given ID if it exists, or ErrUserNotFound otherwise
  854. func (a *Manager) UserByID(id string) (*User, error) {
  855. rows, err := a.db.Query(selectUserByIDQuery, id)
  856. if err != nil {
  857. return nil, err
  858. }
  859. return a.readUser(rows)
  860. }
  861. // UserByStripeCustomer returns the user with the given Stripe customer ID if it exists, or ErrUserNotFound otherwise.
  862. func (a *Manager) UserByStripeCustomer(stripeCustomerID string) (*User, error) {
  863. rows, err := a.db.Query(selectUserByStripeCustomerIDQuery, stripeCustomerID)
  864. if err != nil {
  865. return nil, err
  866. }
  867. return a.readUser(rows)
  868. }
  869. func (a *Manager) userByToken(token string) (*User, error) {
  870. rows, err := a.db.Query(selectUserByTokenQuery, token, time.Now().Unix())
  871. if err != nil {
  872. return nil, err
  873. }
  874. return a.readUser(rows)
  875. }
  876. func (a *Manager) readUser(rows *sql.Rows) (*User, error) {
  877. defer rows.Close()
  878. var id, username, hash, role, prefs, syncTopic string
  879. var stripeCustomerID, stripeSubscriptionID, stripeSubscriptionStatus, stripeSubscriptionInterval, stripeMonthlyPriceID, stripeYearlyPriceID, tierID, tierCode, tierName sql.NullString
  880. var messages, emails, sms, calls int64
  881. var messagesLimit, messagesExpiryDuration, emailsLimit, smsLimit, callsLimit, reservationsLimit, attachmentFileSizeLimit, attachmentTotalSizeLimit, attachmentExpiryDuration, attachmentBandwidthLimit, stripeSubscriptionPaidUntil, stripeSubscriptionCancelAt, deleted sql.NullInt64
  882. if !rows.Next() {
  883. return nil, ErrUserNotFound
  884. }
  885. if err := rows.Scan(&id, &username, &hash, &role, &prefs, &syncTopic, &messages, &emails, &sms, &calls, &stripeCustomerID, &stripeSubscriptionID, &stripeSubscriptionStatus, &stripeSubscriptionInterval, &stripeSubscriptionPaidUntil, &stripeSubscriptionCancelAt, &deleted, &tierID, &tierCode, &tierName, &messagesLimit, &messagesExpiryDuration, &emailsLimit, &smsLimit, &callsLimit, &reservationsLimit, &attachmentFileSizeLimit, &attachmentTotalSizeLimit, &attachmentExpiryDuration, &attachmentBandwidthLimit, &stripeMonthlyPriceID, &stripeYearlyPriceID); err != nil {
  886. return nil, err
  887. } else if err := rows.Err(); err != nil {
  888. return nil, err
  889. }
  890. user := &User{
  891. ID: id,
  892. Name: username,
  893. Hash: hash,
  894. Role: Role(role),
  895. Prefs: &Prefs{},
  896. SyncTopic: syncTopic,
  897. Stats: &Stats{
  898. Messages: messages,
  899. Emails: emails,
  900. SMS: sms,
  901. Calls: calls,
  902. },
  903. Billing: &Billing{
  904. StripeCustomerID: stripeCustomerID.String, // May be empty
  905. StripeSubscriptionID: stripeSubscriptionID.String, // May be empty
  906. StripeSubscriptionStatus: stripe.SubscriptionStatus(stripeSubscriptionStatus.String), // May be empty
  907. StripeSubscriptionInterval: stripe.PriceRecurringInterval(stripeSubscriptionInterval.String), // May be empty
  908. StripeSubscriptionPaidUntil: time.Unix(stripeSubscriptionPaidUntil.Int64, 0), // May be zero
  909. StripeSubscriptionCancelAt: time.Unix(stripeSubscriptionCancelAt.Int64, 0), // May be zero
  910. },
  911. Deleted: deleted.Valid,
  912. }
  913. if err := json.Unmarshal([]byte(prefs), user.Prefs); err != nil {
  914. return nil, err
  915. }
  916. if tierCode.Valid {
  917. // See readTier() when this is changed!
  918. user.Tier = &Tier{
  919. ID: tierID.String,
  920. Code: tierCode.String,
  921. Name: tierName.String,
  922. MessageLimit: messagesLimit.Int64,
  923. MessageExpiryDuration: time.Duration(messagesExpiryDuration.Int64) * time.Second,
  924. EmailLimit: emailsLimit.Int64,
  925. SMSLimit: smsLimit.Int64,
  926. CallLimit: callsLimit.Int64,
  927. ReservationLimit: reservationsLimit.Int64,
  928. AttachmentFileSizeLimit: attachmentFileSizeLimit.Int64,
  929. AttachmentTotalSizeLimit: attachmentTotalSizeLimit.Int64,
  930. AttachmentExpiryDuration: time.Duration(attachmentExpiryDuration.Int64) * time.Second,
  931. AttachmentBandwidthLimit: attachmentBandwidthLimit.Int64,
  932. StripeMonthlyPriceID: stripeMonthlyPriceID.String, // May be empty
  933. StripeYearlyPriceID: stripeYearlyPriceID.String, // May be empty
  934. }
  935. }
  936. return user, nil
  937. }
  938. // Grants returns all user-specific access control entries
  939. func (a *Manager) Grants(username string) ([]Grant, error) {
  940. rows, err := a.db.Query(selectUserAccessQuery, username)
  941. if err != nil {
  942. return nil, err
  943. }
  944. defer rows.Close()
  945. grants := make([]Grant, 0)
  946. for rows.Next() {
  947. var topic string
  948. var read, write bool
  949. if err := rows.Scan(&topic, &read, &write); err != nil {
  950. return nil, err
  951. } else if err := rows.Err(); err != nil {
  952. return nil, err
  953. }
  954. grants = append(grants, Grant{
  955. TopicPattern: fromSQLWildcard(topic),
  956. Allow: NewPermission(read, write),
  957. })
  958. }
  959. return grants, nil
  960. }
  961. // Reservations returns all user-owned topics, and the associated everyone-access
  962. func (a *Manager) Reservations(username string) ([]Reservation, error) {
  963. rows, err := a.db.Query(selectUserReservationsQuery, Everyone, username)
  964. if err != nil {
  965. return nil, err
  966. }
  967. defer rows.Close()
  968. reservations := make([]Reservation, 0)
  969. for rows.Next() {
  970. var topic string
  971. var ownerRead, ownerWrite bool
  972. var everyoneRead, everyoneWrite sql.NullBool
  973. if err := rows.Scan(&topic, &ownerRead, &ownerWrite, &everyoneRead, &everyoneWrite); err != nil {
  974. return nil, err
  975. } else if err := rows.Err(); err != nil {
  976. return nil, err
  977. }
  978. reservations = append(reservations, Reservation{
  979. Topic: topic,
  980. Owner: NewPermission(ownerRead, ownerWrite),
  981. Everyone: NewPermission(everyoneRead.Bool, everyoneWrite.Bool), // false if null
  982. })
  983. }
  984. return reservations, nil
  985. }
  986. // HasReservation returns true if the given topic access is owned by the user
  987. func (a *Manager) HasReservation(username, topic string) (bool, error) {
  988. rows, err := a.db.Query(selectUserHasReservationQuery, username, topic)
  989. if err != nil {
  990. return false, err
  991. }
  992. defer rows.Close()
  993. if !rows.Next() {
  994. return false, errNoRows
  995. }
  996. var count int64
  997. if err := rows.Scan(&count); err != nil {
  998. return false, err
  999. }
  1000. return count > 0, nil
  1001. }
  1002. // ReservationsCount returns the number of reservations owned by this user
  1003. func (a *Manager) ReservationsCount(username string) (int64, error) {
  1004. rows, err := a.db.Query(selectUserReservationsCountQuery, username)
  1005. if err != nil {
  1006. return 0, err
  1007. }
  1008. defer rows.Close()
  1009. if !rows.Next() {
  1010. return 0, errNoRows
  1011. }
  1012. var count int64
  1013. if err := rows.Scan(&count); err != nil {
  1014. return 0, err
  1015. }
  1016. return count, nil
  1017. }
  1018. // ReservationOwner returns user ID of the user that owns this topic, or an
  1019. // empty string if it's not owned by anyone
  1020. func (a *Manager) ReservationOwner(topic string) (string, error) {
  1021. rows, err := a.db.Query(selectUserReservationsOwnerQuery, topic)
  1022. if err != nil {
  1023. return "", err
  1024. }
  1025. defer rows.Close()
  1026. if !rows.Next() {
  1027. return "", nil
  1028. }
  1029. var ownerUserID string
  1030. if err := rows.Scan(&ownerUserID); err != nil {
  1031. return "", err
  1032. }
  1033. return ownerUserID, nil
  1034. }
  1035. // ChangePassword changes a user's password
  1036. func (a *Manager) ChangePassword(username, password string) error {
  1037. hash, err := bcrypt.GenerateFromPassword([]byte(password), a.bcryptCost)
  1038. if err != nil {
  1039. return err
  1040. }
  1041. if _, err := a.db.Exec(updateUserPassQuery, hash, username); err != nil {
  1042. return err
  1043. }
  1044. return nil
  1045. }
  1046. // ChangeRole changes a user's role. When a role is changed from RoleUser to RoleAdmin,
  1047. // all existing access control entries (Grant) are removed, since they are no longer needed.
  1048. func (a *Manager) ChangeRole(username string, role Role) error {
  1049. if !AllowedUsername(username) || !AllowedRole(role) {
  1050. return ErrInvalidArgument
  1051. }
  1052. if _, err := a.db.Exec(updateUserRoleQuery, string(role), username); err != nil {
  1053. return err
  1054. }
  1055. if role == RoleAdmin {
  1056. if _, err := a.db.Exec(deleteUserAccessQuery, username, username); err != nil {
  1057. return err
  1058. }
  1059. }
  1060. return nil
  1061. }
  1062. // ChangeTier changes a user's tier using the tier code. This function does not delete reservations, messages,
  1063. // or attachments, even if the new tier has lower limits in this regard. That has to be done elsewhere.
  1064. func (a *Manager) ChangeTier(username, tier string) error {
  1065. if !AllowedUsername(username) {
  1066. return ErrInvalidArgument
  1067. }
  1068. t, err := a.Tier(tier)
  1069. if err != nil {
  1070. return err
  1071. } else if err := a.checkReservationsLimit(username, t.ReservationLimit); err != nil {
  1072. return err
  1073. }
  1074. if _, err := a.db.Exec(updateUserTierQuery, tier, username); err != nil {
  1075. return err
  1076. }
  1077. return nil
  1078. }
  1079. // ResetTier removes the tier from the given user
  1080. func (a *Manager) ResetTier(username string) error {
  1081. if !AllowedUsername(username) && username != Everyone && username != "" {
  1082. return ErrInvalidArgument
  1083. } else if err := a.checkReservationsLimit(username, 0); err != nil {
  1084. return err
  1085. }
  1086. _, err := a.db.Exec(deleteUserTierQuery, username)
  1087. return err
  1088. }
  1089. func (a *Manager) checkReservationsLimit(username string, reservationsLimit int64) error {
  1090. u, err := a.User(username)
  1091. if err != nil {
  1092. return err
  1093. }
  1094. if u.Tier != nil && reservationsLimit < u.Tier.ReservationLimit {
  1095. reservations, err := a.Reservations(username)
  1096. if err != nil {
  1097. return err
  1098. } else if int64(len(reservations)) > reservationsLimit {
  1099. return ErrTooManyReservations
  1100. }
  1101. }
  1102. return nil
  1103. }
  1104. // AllowReservation tests if a user may create an access control entry for the given topic.
  1105. // If there are any ACL entries that are not owned by the user, an error is returned.
  1106. func (a *Manager) AllowReservation(username string, topic string) error {
  1107. if (!AllowedUsername(username) && username != Everyone) || !AllowedTopic(topic) {
  1108. return ErrInvalidArgument
  1109. }
  1110. rows, err := a.db.Query(selectOtherAccessCountQuery, topic, topic, username)
  1111. if err != nil {
  1112. return err
  1113. }
  1114. defer rows.Close()
  1115. if !rows.Next() {
  1116. return errNoRows
  1117. }
  1118. var otherCount int
  1119. if err := rows.Scan(&otherCount); err != nil {
  1120. return err
  1121. }
  1122. if otherCount > 0 {
  1123. return errTopicOwnedByOthers
  1124. }
  1125. return nil
  1126. }
  1127. // AllowAccess adds or updates an entry in th access control list for a specific user. It controls
  1128. // read/write access to a topic. The parameter topicPattern may include wildcards (*). The ACL entry
  1129. // owner may either be a user (username), or the system (empty).
  1130. func (a *Manager) AllowAccess(username string, topicPattern string, permission Permission) error {
  1131. if !AllowedUsername(username) && username != Everyone {
  1132. return ErrInvalidArgument
  1133. } else if !AllowedTopicPattern(topicPattern) {
  1134. return ErrInvalidArgument
  1135. }
  1136. owner := ""
  1137. if _, err := a.db.Exec(upsertUserAccessQuery, username, toSQLWildcard(topicPattern), permission.IsRead(), permission.IsWrite(), owner, owner); err != nil {
  1138. return err
  1139. }
  1140. return nil
  1141. }
  1142. // ResetAccess removes an access control list entry for a specific username/topic, or (if topic is
  1143. // empty) for an entire user. The parameter topicPattern may include wildcards (*).
  1144. func (a *Manager) ResetAccess(username string, topicPattern string) error {
  1145. if !AllowedUsername(username) && username != Everyone && username != "" {
  1146. return ErrInvalidArgument
  1147. } else if !AllowedTopicPattern(topicPattern) && topicPattern != "" {
  1148. return ErrInvalidArgument
  1149. }
  1150. if username == "" && topicPattern == "" {
  1151. _, err := a.db.Exec(deleteAllAccessQuery, username)
  1152. return err
  1153. } else if topicPattern == "" {
  1154. _, err := a.db.Exec(deleteUserAccessQuery, username, username)
  1155. return err
  1156. }
  1157. _, err := a.db.Exec(deleteTopicAccessQuery, username, username, toSQLWildcard(topicPattern))
  1158. return err
  1159. }
  1160. // AddReservation creates two access control entries for the given topic: one with full read/write access for the
  1161. // given user, and one for Everyone with the permission passed as everyone. The user also owns the entries, and
  1162. // can modify or delete them.
  1163. func (a *Manager) AddReservation(username string, topic string, everyone Permission) error {
  1164. if !AllowedUsername(username) || username == Everyone || !AllowedTopic(topic) {
  1165. return ErrInvalidArgument
  1166. }
  1167. tx, err := a.db.Begin()
  1168. if err != nil {
  1169. return err
  1170. }
  1171. defer tx.Rollback()
  1172. if _, err := tx.Exec(upsertUserAccessQuery, username, topic, true, true, username, username); err != nil {
  1173. return err
  1174. }
  1175. if _, err := tx.Exec(upsertUserAccessQuery, Everyone, topic, everyone.IsRead(), everyone.IsWrite(), username, username); err != nil {
  1176. return err
  1177. }
  1178. return tx.Commit()
  1179. }
  1180. // RemoveReservations deletes the access control entries associated with the given username/topic, as
  1181. // well as all entries with Everyone/topic. This is the counterpart for AddReservation.
  1182. func (a *Manager) RemoveReservations(username string, topics ...string) error {
  1183. if !AllowedUsername(username) || username == Everyone || len(topics) == 0 {
  1184. return ErrInvalidArgument
  1185. }
  1186. for _, topic := range topics {
  1187. if !AllowedTopic(topic) {
  1188. return ErrInvalidArgument
  1189. }
  1190. }
  1191. tx, err := a.db.Begin()
  1192. if err != nil {
  1193. return err
  1194. }
  1195. defer tx.Rollback()
  1196. for _, topic := range topics {
  1197. if _, err := tx.Exec(deleteTopicAccessQuery, username, username, topic); err != nil {
  1198. return err
  1199. }
  1200. if _, err := tx.Exec(deleteTopicAccessQuery, Everyone, Everyone, topic); err != nil {
  1201. return err
  1202. }
  1203. }
  1204. return tx.Commit()
  1205. }
  1206. // DefaultAccess returns the default read/write access if no access control entry matches
  1207. func (a *Manager) DefaultAccess() Permission {
  1208. return a.defaultAccess
  1209. }
  1210. // AddTier creates a new tier in the database
  1211. func (a *Manager) AddTier(tier *Tier) error {
  1212. if tier.ID == "" {
  1213. tier.ID = util.RandomStringPrefix(tierIDPrefix, tierIDLength)
  1214. }
  1215. if _, err := a.db.Exec(insertTierQuery, tier.ID, tier.Code, tier.Name, tier.MessageLimit, int64(tier.MessageExpiryDuration.Seconds()), tier.EmailLimit, tier.SMSLimit, tier.CallLimit, tier.ReservationLimit, tier.AttachmentFileSizeLimit, tier.AttachmentTotalSizeLimit, int64(tier.AttachmentExpiryDuration.Seconds()), tier.AttachmentBandwidthLimit, nullString(tier.StripeMonthlyPriceID), nullString(tier.StripeYearlyPriceID)); err != nil {
  1216. return err
  1217. }
  1218. return nil
  1219. }
  1220. // UpdateTier updates a tier's properties in the database
  1221. func (a *Manager) UpdateTier(tier *Tier) error {
  1222. if _, err := a.db.Exec(updateTierQuery, tier.Name, tier.MessageLimit, int64(tier.MessageExpiryDuration.Seconds()), tier.EmailLimit, tier.SMSLimit, tier.CallLimit, tier.ReservationLimit, tier.AttachmentFileSizeLimit, tier.AttachmentTotalSizeLimit, int64(tier.AttachmentExpiryDuration.Seconds()), tier.AttachmentBandwidthLimit, nullString(tier.StripeMonthlyPriceID), nullString(tier.StripeYearlyPriceID), tier.Code); err != nil {
  1223. return err
  1224. }
  1225. return nil
  1226. }
  1227. // RemoveTier deletes the tier with the given code
  1228. func (a *Manager) RemoveTier(code string) error {
  1229. if !AllowedTier(code) {
  1230. return ErrInvalidArgument
  1231. }
  1232. // This fails if any user has this tier
  1233. if _, err := a.db.Exec(deleteTierQuery, code); err != nil {
  1234. return err
  1235. }
  1236. return nil
  1237. }
  1238. // ChangeBilling updates a user's billing fields, namely the Stripe customer ID, and subscription information
  1239. func (a *Manager) ChangeBilling(username string, billing *Billing) error {
  1240. if _, err := a.db.Exec(updateBillingQuery, nullString(billing.StripeCustomerID), nullString(billing.StripeSubscriptionID), nullString(string(billing.StripeSubscriptionStatus)), nullString(string(billing.StripeSubscriptionInterval)), nullInt64(billing.StripeSubscriptionPaidUntil.Unix()), nullInt64(billing.StripeSubscriptionCancelAt.Unix()), username); err != nil {
  1241. return err
  1242. }
  1243. return nil
  1244. }
  1245. // Tiers returns a list of all Tier structs
  1246. func (a *Manager) Tiers() ([]*Tier, error) {
  1247. rows, err := a.db.Query(selectTiersQuery)
  1248. if err != nil {
  1249. return nil, err
  1250. }
  1251. defer rows.Close()
  1252. tiers := make([]*Tier, 0)
  1253. for {
  1254. tier, err := a.readTier(rows)
  1255. if err == ErrTierNotFound {
  1256. break
  1257. } else if err != nil {
  1258. return nil, err
  1259. }
  1260. tiers = append(tiers, tier)
  1261. }
  1262. return tiers, nil
  1263. }
  1264. // Tier returns a Tier based on the code, or ErrTierNotFound if it does not exist
  1265. func (a *Manager) Tier(code string) (*Tier, error) {
  1266. rows, err := a.db.Query(selectTierByCodeQuery, code)
  1267. if err != nil {
  1268. return nil, err
  1269. }
  1270. defer rows.Close()
  1271. return a.readTier(rows)
  1272. }
  1273. // TierByStripePrice returns a Tier based on the Stripe price ID, or ErrTierNotFound if it does not exist
  1274. func (a *Manager) TierByStripePrice(priceID string) (*Tier, error) {
  1275. rows, err := a.db.Query(selectTierByPriceIDQuery, priceID, priceID)
  1276. if err != nil {
  1277. return nil, err
  1278. }
  1279. defer rows.Close()
  1280. return a.readTier(rows)
  1281. }
  1282. func (a *Manager) readTier(rows *sql.Rows) (*Tier, error) {
  1283. var id, code, name string
  1284. var stripeMonthlyPriceID, stripeYearlyPriceID sql.NullString
  1285. var messagesLimit, messagesExpiryDuration, emailsLimit, smsLimit, callsLimit, reservationsLimit, attachmentFileSizeLimit, attachmentTotalSizeLimit, attachmentExpiryDuration, attachmentBandwidthLimit sql.NullInt64
  1286. if !rows.Next() {
  1287. return nil, ErrTierNotFound
  1288. }
  1289. if err := rows.Scan(&id, &code, &name, &messagesLimit, &messagesExpiryDuration, &emailsLimit, &smsLimit, &callsLimit, &reservationsLimit, &attachmentFileSizeLimit, &attachmentTotalSizeLimit, &attachmentExpiryDuration, &attachmentBandwidthLimit, &stripeMonthlyPriceID, &stripeYearlyPriceID); err != nil {
  1290. return nil, err
  1291. } else if err := rows.Err(); err != nil {
  1292. return nil, err
  1293. }
  1294. // When changed, note readUser() as well
  1295. return &Tier{
  1296. ID: id,
  1297. Code: code,
  1298. Name: name,
  1299. MessageLimit: messagesLimit.Int64,
  1300. MessageExpiryDuration: time.Duration(messagesExpiryDuration.Int64) * time.Second,
  1301. EmailLimit: emailsLimit.Int64,
  1302. SMSLimit: smsLimit.Int64,
  1303. CallLimit: callsLimit.Int64,
  1304. ReservationLimit: reservationsLimit.Int64,
  1305. AttachmentFileSizeLimit: attachmentFileSizeLimit.Int64,
  1306. AttachmentTotalSizeLimit: attachmentTotalSizeLimit.Int64,
  1307. AttachmentExpiryDuration: time.Duration(attachmentExpiryDuration.Int64) * time.Second,
  1308. AttachmentBandwidthLimit: attachmentBandwidthLimit.Int64,
  1309. StripeMonthlyPriceID: stripeMonthlyPriceID.String, // May be empty
  1310. StripeYearlyPriceID: stripeYearlyPriceID.String, // May be empty
  1311. }, nil
  1312. }
  1313. // Close closes the underlying database
  1314. func (a *Manager) Close() error {
  1315. return a.db.Close()
  1316. }
  1317. func toSQLWildcard(s string) string {
  1318. return strings.ReplaceAll(s, "*", "%")
  1319. }
  1320. func fromSQLWildcard(s string) string {
  1321. return strings.ReplaceAll(s, "%", "*")
  1322. }
  1323. func runStartupQueries(db *sql.DB, startupQueries string) error {
  1324. if _, err := db.Exec(startupQueries); err != nil {
  1325. return err
  1326. }
  1327. if _, err := db.Exec(builtinStartupQueries); err != nil {
  1328. return err
  1329. }
  1330. return nil
  1331. }
  1332. func setupDB(db *sql.DB) error {
  1333. // If 'schemaVersion' table does not exist, this must be a new database
  1334. rowsSV, err := db.Query(selectSchemaVersionQuery)
  1335. if err != nil {
  1336. return setupNewDB(db)
  1337. }
  1338. defer rowsSV.Close()
  1339. // If 'schemaVersion' table exists, read version and potentially upgrade
  1340. schemaVersion := 0
  1341. if !rowsSV.Next() {
  1342. return errors.New("cannot determine schema version: database file may be corrupt")
  1343. }
  1344. if err := rowsSV.Scan(&schemaVersion); err != nil {
  1345. return err
  1346. }
  1347. rowsSV.Close()
  1348. // Do migrations
  1349. if schemaVersion == currentSchemaVersion {
  1350. return nil
  1351. } else if schemaVersion > currentSchemaVersion {
  1352. return fmt.Errorf("unexpected schema version: version %d is higher than current version %d", schemaVersion, currentSchemaVersion)
  1353. }
  1354. for i := schemaVersion; i < currentSchemaVersion; i++ {
  1355. fn, ok := migrations[i]
  1356. if !ok {
  1357. return fmt.Errorf("cannot find migration step from schema version %d to %d", i, i+1)
  1358. } else if err := fn(db); err != nil {
  1359. return err
  1360. }
  1361. }
  1362. return nil
  1363. }
  1364. func setupNewDB(db *sql.DB) error {
  1365. if _, err := db.Exec(createTablesQueries); err != nil {
  1366. return err
  1367. }
  1368. if _, err := db.Exec(insertSchemaVersion, currentSchemaVersion); err != nil {
  1369. return err
  1370. }
  1371. return nil
  1372. }
  1373. func migrateFrom1(db *sql.DB) error {
  1374. log.Tag(tag).Info("Migrating user database schema: from 1 to 2")
  1375. tx, err := db.Begin()
  1376. if err != nil {
  1377. return err
  1378. }
  1379. defer tx.Rollback()
  1380. // Rename user -> user_old, and create new tables
  1381. if _, err := tx.Exec(migrate1To2CreateTablesQueries); err != nil {
  1382. return err
  1383. }
  1384. // Insert users from user_old into new user table, with ID and sync_topic
  1385. rows, err := tx.Query(migrate1To2SelectAllOldUsernamesNoTx)
  1386. if err != nil {
  1387. return err
  1388. }
  1389. defer rows.Close()
  1390. usernames := make([]string, 0)
  1391. for rows.Next() {
  1392. var username string
  1393. if err := rows.Scan(&username); err != nil {
  1394. return err
  1395. }
  1396. usernames = append(usernames, username)
  1397. }
  1398. if err := rows.Close(); err != nil {
  1399. return err
  1400. }
  1401. for _, username := range usernames {
  1402. userID := util.RandomStringPrefix(userIDPrefix, userIDLength)
  1403. syncTopic := util.RandomStringPrefix(syncTopicPrefix, syncTopicLength)
  1404. if _, err := tx.Exec(migrate1To2InsertUserNoTx, userID, syncTopic, username); err != nil {
  1405. return err
  1406. }
  1407. }
  1408. // Migrate old "access" table to "user_access" and drop "access" and "user_old"
  1409. if _, err := tx.Exec(migrate1To2InsertFromOldTablesAndDropNoTx); err != nil {
  1410. return err
  1411. }
  1412. if _, err := tx.Exec(updateSchemaVersion, 2); err != nil {
  1413. return err
  1414. }
  1415. if err := tx.Commit(); err != nil {
  1416. return err
  1417. }
  1418. return nil
  1419. }
  1420. func migrateFrom2(db *sql.DB) error {
  1421. log.Tag(tag).Info("Migrating user database schema: from 2 to 3")
  1422. tx, err := db.Begin()
  1423. if err != nil {
  1424. return err
  1425. }
  1426. defer tx.Rollback()
  1427. if _, err := tx.Exec(migrate2To3UpdateQueries); err != nil {
  1428. return err
  1429. }
  1430. if _, err := tx.Exec(updateSchemaVersion, 3); err != nil {
  1431. return err
  1432. }
  1433. return tx.Commit()
  1434. }
  1435. func migrateFrom3(db *sql.DB) error {
  1436. log.Tag(tag).Info("Migrating user database schema: from 3 to 4")
  1437. tx, err := db.Begin()
  1438. if err != nil {
  1439. return err
  1440. }
  1441. defer tx.Rollback()
  1442. if _, err := tx.Exec(migrate3To4UpdateQueries); err != nil {
  1443. return err
  1444. }
  1445. if _, err := tx.Exec(updateSchemaVersion, 4); err != nil {
  1446. return err
  1447. }
  1448. return tx.Commit()
  1449. }
  1450. func nullString(s string) sql.NullString {
  1451. if s == "" {
  1452. return sql.NullString{}
  1453. }
  1454. return sql.NullString{String: s, Valid: true}
  1455. }
  1456. func nullInt64(v int64) sql.NullInt64 {
  1457. if v == 0 {
  1458. return sql.NullInt64{}
  1459. }
  1460. return sql.NullInt64{Int64: v, Valid: true}
  1461. }