db.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. package config
  2. import (
  3. "database/sql"
  4. "errors"
  5. "io"
  6. "net"
  7. "git.aetherial.dev/aeth/yosai/pkg/daemon"
  8. "github.com/mattn/go-sqlite3"
  9. )
  10. var (
  11. ErrDuplicate = errors.New("record already exists")
  12. ErrNotExists = errors.New("row not exists")
  13. ErrUpdateFailed = errors.New("update failed")
  14. ErrDeleteFailed = errors.New("delete failed")
  15. )
  16. type Username string
  17. func ValidateUsername(name string) Username {
  18. return Username(name)
  19. }
  20. type User struct {
  21. Name Username
  22. Id int
  23. }
  24. type DatabaseIO interface {
  25. Migrate()
  26. AddUser(Username) (User, error)
  27. UpdateUser(Username, daemon.Configuration) error
  28. Log(...string)
  29. GetConfigByUser(Username) (daemon.Configuration, error)
  30. }
  31. type SQLiteRepo struct {
  32. db *sql.DB
  33. out io.Writer
  34. }
  35. /*
  36. Create a new SQL lite repo
  37. :param db: a pointer to a sql.DB to write the database into
  38. */
  39. func NewSQLiteRepo(db *sql.DB, out io.Writer) *SQLiteRepo {
  40. return &SQLiteRepo{
  41. db: db,
  42. out: out,
  43. }
  44. }
  45. func (s *SQLiteRepo) Log(msg ...string) {
  46. logMsg := "SQL Lite log:"
  47. for i := range msg {
  48. logMsg = logMsg + msg[i]
  49. }
  50. logMsg = logMsg + "\n"
  51. s.out.Write([]byte(logMsg))
  52. }
  53. func (s *SQLiteRepo) Migrate() {
  54. userTable := `
  55. CREATE TABLE IF NOT EXISTS users(
  56. id INTEGER PRIMARY KEY AUTOINCREMENT,
  57. name TEXT NOT NULL
  58. );
  59. `
  60. cloudTable := `
  61. CREATE TABLE IF NOT EXISTS cloud(
  62. user_id INTEGER NOT NULL,
  63. image TEXT NOT NULL,
  64. region TEXT NOT NULL,
  65. linode_type TEXT NOT NULL
  66. );
  67. `
  68. ansibleTable := `
  69. CREATE TABLE IF NOT EXISTS ansible(
  70. user_id INTEGER NOT NULL,
  71. repo_url TEXT NOT NULL,
  72. branch TEXT NOT NULL,
  73. playbook_name TEXT NOT NULL,
  74. ansible_backend TEXT NOT NULL,
  75. ansible_backend_url TEXT NOT NULL
  76. );
  77. `
  78. serverTable := `
  79. CREATE TABLE IF NOT EXISTS servers(
  80. user_id INTEGER NOT NULL,
  81. name TEXT NOT NULL,
  82. wan_ipv4 TEXT NOT NULL,
  83. vpn_ipv4 TEXT NOT NULL,
  84. port INTEGER NOT NULL
  85. );
  86. `
  87. clientTable := `
  88. CREATE TABLE IF NOT EXISTS clients(
  89. user_id INTEGER NOT NULL,
  90. name TEXT NOT NULL,
  91. pubkey TEXT NOT NULL,
  92. vpn_ipv4 TEXT NOT NULL,
  93. default_client INTEGER NOT NULL
  94. );
  95. `
  96. vpnTable := `
  97. CREATE TABLE IF NOT EXISTS vpn(
  98. user_id INTEGER NOT NULL,
  99. vpn_ip TEXT NOT NULL,
  100. vpn_subnet_mask INTEGER NOT NULL
  101. );
  102. `
  103. queries := []string{
  104. userTable,
  105. cloudTable,
  106. ansibleTable,
  107. serverTable,
  108. clientTable,
  109. vpnTable,
  110. }
  111. for i := range queries {
  112. _, err := s.db.Exec(queries[i])
  113. if err != nil {
  114. s.Log(err.Error())
  115. }
  116. }
  117. }
  118. /*
  119. Retrieve a user struct from, querying by their username
  120. :param name: the username of the querying user Note -> must validate the username before calling
  121. */
  122. func (s *SQLiteRepo) getUser(name Username) (User, error) {
  123. row := s.db.QueryRow("SELECT * FROM users WHERE name = ?", name)
  124. var user User
  125. if err := row.Scan(&user.Id, &user.Name); err != nil {
  126. if errors.Is(err, sql.ErrNoRows) {
  127. return user, ErrNotExists
  128. }
  129. return user, err
  130. }
  131. return user, nil
  132. }
  133. /*
  134. Update all of the data for a users configuration
  135. :param config: a daemon.Configuration to put into the database
  136. :param user: the User struct representing the calling user
  137. */
  138. func (s *SQLiteRepo) UpdateUser(username Username, config daemon.Configuration) error {
  139. trx, err := s.db.Begin()
  140. if err != nil {
  141. s.Log("Error creating DB transaction: ", err.Error())
  142. return err
  143. }
  144. defer trx.Rollback()
  145. user, err := s.getUser(username)
  146. if err != nil {
  147. s.Log("Error getting the user: ", string(username), err.Error())
  148. return err
  149. }
  150. _, err = trx.Exec("UPDATE cloud SET user_id = ?, image = ?, region = ?, linode_type = ? WHERE user_id = ?",
  151. user.Id,
  152. config.Cloud.Image,
  153. config.Cloud.Region,
  154. config.Cloud.LinodeType,
  155. user.Id)
  156. if err != nil {
  157. return err
  158. }
  159. _, err = trx.Exec("UPDATE ansible SET user_id = ?, repo_url = ?, branch = ?, playbook_name = ?, ansible_backend = ?, ansible_backend_url = ? WHERE user_id = ?",
  160. user.Id,
  161. config.Ansible.Repo,
  162. config.Ansible.Branch,
  163. config.Ansible.PlaybookName,
  164. config.Service.AnsibleBackend,
  165. config.Service.AnsibleBackendUrl,
  166. user.Id)
  167. if err != nil {
  168. return err
  169. }
  170. for i := range config.Service.Servers {
  171. server := config.Service.Servers[i]
  172. _, err := trx.Exec("UPDATE servers SET user_id = ?, name = ?, wan_ipv4 = ?, vpn_ip = ?, port = ? WHERE user_id = ? AND name = ?",
  173. user.Id,
  174. server.Name,
  175. server.WanIpv4,
  176. server.VpnIpv4,
  177. server.Port,
  178. user.Id,
  179. server.Name)
  180. if err != nil {
  181. return err
  182. }
  183. }
  184. for i := range config.Service.Clients {
  185. client := config.Service.Clients[i]
  186. _, err := trx.Exec("UPDATE clients SET user_id = ?, name = ?, pubkey = ?, vpn_ipv4 = ?, default_client = ? WHERE user_id = ? AND name = ?",
  187. user.Id,
  188. client.Name,
  189. client.Pubkey,
  190. client.VpnIpv4,
  191. client.Default,
  192. user.Id,
  193. client.Name)
  194. if err != nil {
  195. return err
  196. }
  197. }
  198. err = trx.Commit()
  199. if err != nil {
  200. return err
  201. }
  202. return nil
  203. }
  204. /*
  205. Create an entry in the vpn information table
  206. :param user: the calling User
  207. :param config: the daemon.Configuration with the configuration data
  208. */
  209. func (s *SQLiteRepo) insertVpnInfo(user User, config daemon.Configuration) error {
  210. trx, err := s.db.Begin()
  211. if err != nil {
  212. s.Log("Failed to start DB transaction: ", err.Error())
  213. return err
  214. }
  215. defer trx.Rollback()
  216. _, err = trx.Exec("INSERT INTO vpn(user_id, vpn_ip, vpn_subnet_mask) values(?,?,?)",
  217. user.Id,
  218. config.Service.VpnAddressSpace.String(),
  219. config.Service.VpnMask)
  220. if err != nil {
  221. var sqliteErr sqlite3.Error
  222. if errors.As(err, &sqliteErr) {
  223. if errors.Is(sqliteErr.ExtendedCode, sqlite3.ErrConstraintUnique) {
  224. return ErrDuplicate
  225. }
  226. }
  227. return err
  228. }
  229. err = trx.Commit()
  230. if err != nil {
  231. return err
  232. }
  233. return nil
  234. }
  235. /*
  236. Create an entry in the client table for a user
  237. :param user: the calling User
  238. :param cloudConfig: the cloud specific configuration for the user
  239. */
  240. func (s *SQLiteRepo) insertClient(user User, config daemon.Configuration) error {
  241. trx, err := s.db.Begin()
  242. if err != nil {
  243. s.Log("Failed to start DB transaction: ", err.Error())
  244. return err
  245. }
  246. defer trx.Rollback()
  247. for i := range config.Service.Clients {
  248. client := config.Service.Clients[i]
  249. _, err = trx.Exec("INSERT INTO clients(user_id, name, pubkey, vpn_ipv4, default_client) values(?,?,?,?,?)",
  250. user.Id,
  251. client.Name,
  252. client.Pubkey,
  253. client.VpnIpv4,
  254. client.Default)
  255. if err != nil {
  256. var sqliteErr sqlite3.Error
  257. if errors.As(err, &sqliteErr) {
  258. if errors.Is(sqliteErr.ExtendedCode, sqlite3.ErrConstraintUnique) {
  259. return ErrDuplicate
  260. }
  261. }
  262. return err
  263. }
  264. }
  265. err = trx.Commit()
  266. if err != nil {
  267. return err
  268. }
  269. return nil
  270. }
  271. /*
  272. Create an entry in the server table for a user
  273. :param user: the calling User
  274. :param cloudConfig: the cloud specific configuration for the user
  275. */
  276. func (s *SQLiteRepo) insertServer(user User, config daemon.Configuration) error {
  277. trx, err := s.db.Begin()
  278. if err != nil {
  279. s.Log("Failed to start DB transaction: ", err.Error())
  280. return err
  281. }
  282. defer trx.Rollback()
  283. for i := range config.Service.Servers {
  284. server := config.Service.Servers[i]
  285. _, err = trx.Exec("INSERT INTO servers(user_id, name, wan_ipv4, vpn_ipv4, port) values(?,?,?,?,?)",
  286. user.Id,
  287. server.Name,
  288. server.WanIpv4,
  289. server.VpnIpv4,
  290. server.Port)
  291. if err != nil {
  292. var sqliteErr sqlite3.Error
  293. if errors.As(err, &sqliteErr) {
  294. if errors.Is(sqliteErr.ExtendedCode, sqlite3.ErrConstraintUnique) {
  295. return ErrDuplicate
  296. }
  297. }
  298. return err
  299. }
  300. }
  301. err = trx.Commit()
  302. if err != nil {
  303. return err
  304. }
  305. return nil
  306. }
  307. /*
  308. Create an entry in the ansible table for a user
  309. :param user: the calling User
  310. :param cloudConfig: the cloud specific configuration for the user
  311. */
  312. func (s *SQLiteRepo) insertUserAnsible(user User, config daemon.Configuration) error {
  313. trx, err := s.db.Begin()
  314. if err != nil {
  315. s.Log("Failed to start DB transaction: ", err.Error())
  316. return err
  317. }
  318. defer trx.Rollback()
  319. _, err = trx.Exec("INSERT INTO ansible(user_id, repo_url, branch, playbook_name, ansible_backend, ansible_backend_url) values(?,?,?,?,?,?)",
  320. user.Id,
  321. config.Ansible.Repo,
  322. config.Ansible.Branch,
  323. config.Ansible.PlaybookName,
  324. config.Service.AnsibleBackend,
  325. config.Service.AnsibleBackendUrl)
  326. if err != nil {
  327. var sqliteErr sqlite3.Error
  328. if errors.As(err, &sqliteErr) {
  329. if errors.Is(sqliteErr.ExtendedCode, sqlite3.ErrConstraintUnique) {
  330. return ErrDuplicate
  331. }
  332. }
  333. return err
  334. }
  335. err = trx.Commit()
  336. if err != nil {
  337. return err
  338. }
  339. return nil
  340. }
  341. /*
  342. Create an entry in the cloud table for a user
  343. :param user: the calling User
  344. :param cloudConfig: the cloud specific configuration for the user
  345. */
  346. func (s *SQLiteRepo) insertUserCloud(user User, config daemon.Configuration) error {
  347. trx, err := s.db.Begin()
  348. if err != nil {
  349. s.Log("Failed to start DB transaction: ", err.Error())
  350. return err
  351. }
  352. defer trx.Rollback()
  353. _, err = trx.Exec("INSERT INTO cloud(user_id, image, region, linode_type) values(?,?,?,?)",
  354. user.Id,
  355. config.Cloud.Image,
  356. config.Cloud.Region,
  357. config.Cloud.LinodeType)
  358. if err != nil {
  359. var sqliteErr sqlite3.Error
  360. if errors.As(err, &sqliteErr) {
  361. if errors.Is(sqliteErr.ExtendedCode, sqlite3.ErrConstraintUnique) {
  362. return ErrDuplicate
  363. }
  364. }
  365. return err
  366. }
  367. err = trx.Commit()
  368. if err != nil {
  369. return err
  370. }
  371. return nil
  372. }
  373. /*
  374. Populate the different db tables with the users configuration
  375. :param user: the calling user
  376. :param config: the daemon.Configuration to populate into the db
  377. */
  378. func (s *SQLiteRepo) SeedUser(user User, config daemon.Configuration) error {
  379. seedFuncs := []func(User, daemon.Configuration) error{
  380. s.insertClient,
  381. s.insertServer,
  382. s.insertUserAnsible,
  383. s.insertUserCloud,
  384. s.insertVpnInfo,
  385. }
  386. for i := range seedFuncs {
  387. err := seedFuncs[i](user, config)
  388. if err != nil {
  389. return err
  390. }
  391. }
  392. return nil
  393. }
  394. /*
  395. Add a user to the database and return a User struct
  396. :param name: the name of the user
  397. */
  398. func (s *SQLiteRepo) AddUser(name Username) (User, error) {
  399. var user User
  400. res, err := s.db.Exec("INSERT INTO users(name) values(?)", name)
  401. if err != nil {
  402. var sqliteErr sqlite3.Error
  403. if errors.As(err, &sqliteErr) {
  404. if errors.Is(sqliteErr.ExtendedCode, sqlite3.ErrConstraintUnique) {
  405. return user, ErrDuplicate
  406. }
  407. }
  408. return user, err
  409. }
  410. id, err := res.LastInsertId()
  411. if err != nil {
  412. return user, err
  413. }
  414. return User{Name: name, Id: int(id)}, nil
  415. }
  416. /*
  417. Get the configuration for the passed user
  418. :param user: the calling user
  419. */
  420. func (s *SQLiteRepo) GetConfigByUser(username Username) (daemon.Configuration, error) {
  421. config := daemon.NewConfiguration()
  422. user, err := s.getUser(username)
  423. if err != nil {
  424. return *config, err
  425. }
  426. row := s.db.QueryRow("SELECT * FROM cloud WHERE user_id = ?", user.Id)
  427. if err := row.Scan(&user.Id, &config.Cloud.Image, &config.Cloud.Region, &config.Cloud.LinodeType); err != nil {
  428. if errors.Is(err, sql.ErrNoRows) {
  429. return *config, ErrNotExists
  430. }
  431. return *config, err
  432. }
  433. row = s.db.QueryRow("SELECT * FROM ansible WHERE user_id = ?", user.Id)
  434. if err := row.Scan(
  435. &user.Id,
  436. &config.Ansible.Repo,
  437. &config.Ansible.Branch,
  438. &config.Ansible.PlaybookName,
  439. &config.Service.AnsibleBackend,
  440. &config.Service.AnsibleBackendUrl); err != nil {
  441. if errors.Is(err, sql.ErrNoRows) {
  442. return *config, ErrNotExists
  443. }
  444. return *config, err
  445. }
  446. rows, err := s.db.Query("SELECT * FROM servers WHERE user_id = ?", user.Id)
  447. if err != nil {
  448. return *config, err
  449. }
  450. for rows.Next() {
  451. var server daemon.VpnServer
  452. if err := rows.Scan(&user.Id, &server.Name, &server.WanIpv4, &server.VpnIpv4, &server.Port); err != nil {
  453. return *config, err
  454. }
  455. config.Service.Servers[server.Name] = server
  456. }
  457. if err = rows.Err(); err != nil {
  458. return *config, err
  459. }
  460. rows, err = s.db.Query("SELECT * FROM clients WHERE user_id = ?", user.Id)
  461. if err != nil {
  462. return *config, err
  463. }
  464. for rows.Next() {
  465. var client daemon.VpnClient
  466. if err := rows.Scan(&user.Id, &client.Name, &client.Pubkey, &client.VpnIpv4, &client.Default); err != nil {
  467. return *config, err
  468. }
  469. config.Service.Clients[client.Name] = client
  470. }
  471. row = s.db.QueryRow("SELECT * FROM vpn WHERE user_id = ?", user.Id)
  472. var vpnIp string
  473. if err = row.Scan(&user.Id, &vpnIp, &config.Service.VpnMask); err != nil {
  474. return *config, err
  475. }
  476. _, vpnIpv4, _ := net.ParseCIDR(vpnIp)
  477. config.Service.VpnAddressSpace = *vpnIpv4
  478. return *config, nil
  479. }