From 9a50ccd2fcfaa6e5577310945a72ce342ddc149c Mon Sep 17 00:00:00 2001 From: Meutel Date: Sat, 9 Sep 2017 17:12:37 +0200 Subject: [PATCH] Users database --- .gitignore | 1 + README.md | 8 ++++++++ bouquins/auth.go | 13 +++++++------ bouquins/bouquins.go | 13 +++++++++++-- bouquins/db.go | 14 +++++++++++++- bouquins/dbusers.go | 11 +++++++++++ main.go | 36 ++++++++++++++++++++++++------------ 7 files changed, 75 insertions(+), 21 deletions(-) create mode 100644 bouquins/dbusers.go diff --git a/.gitignore b/.gitignore index ee3b468..fb919ac 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ calibre.db bouquins.json Gopkg.lock vendor/ +users.db diff --git a/README.md b/README.md index a461bf1..fd5ce4f 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ Bouquins in Go * tests * auth downloads * csrf +* userdb commands (init, migrate, add/remove user/email) ## Minify JS @@ -47,6 +48,7 @@ Options: * calibre-path path to calibre data * db-path path to calibre SQLite database (default /metadata.db) +* user-db-path path to users SQLite database (default ./users.db) * bind-address HTTP socket bind address * prod (boolean) use minified javascript/CSS * cookie-secret random string for cookie encryption @@ -55,3 +57,9 @@ Options: * name provider name * client-id OAuth client ID * client-secret OAuth secret + +## Users SQL + +CREATE TABLE accounts (id varchar(36) PRIMARY KEY NOT NULL, name varchar(255) NOT NULL); +CREATE TABLE authentifiers (id varchar(36) NOT NULL, authentifier varchar(320) PRIMARY KEY NOT NULL, FOREIGN KEY(id) REFERENCES account(id)); + diff --git a/bouquins/auth.go b/bouquins/auth.go index d6d7fec..59af111 100644 --- a/bouquins/auth.go +++ b/bouquins/auth.go @@ -127,11 +127,12 @@ func (app *Bouquins) CallbackPage(res http.ResponseWriter, req *http.Request) er if err != nil { return err } - // FIXME list allowed users - if userEmail == "meutel@gmail.com" || userEmail == "meutel+github@meutel.net" { - app.SessionSet(sessionUser, "Meutel", res, req) - log.Println("User logged in", userEmail) - return RedirectHome(res, req) + user, err := Account(userEmail) + if err != nil { + log.Println("Error loading user", err) + return fmt.Errorf("Unknown user") } - return fmt.Errorf("Unknown user") + app.SessionSet(sessionUser, user.DisplayName, res, req) + log.Println("User logged in", user.DisplayName) + return RedirectHome(res, req) } diff --git a/bouquins/bouquins.go b/bouquins/bouquins.go index 21064d2..6a19344 100644 --- a/bouquins/bouquins.go +++ b/bouquins/bouquins.go @@ -71,6 +71,7 @@ type Conf struct { DbPath string `json:"db-path"` CalibrePath string `json:"calibre-path"` Prod bool `json:"prod"` + UserDbPath string `json:"user-db-path"` CookieSecret string `json:"cookie-secret"` ExternalURL string `json:"external-url"` ProvidersConf []ProviderConf `json:"providers"` @@ -85,12 +86,20 @@ type ProviderConf struct { // Bouquins contains application common resources: templates, database type Bouquins struct { - Tpl *template.Template - DB *sql.DB + Tpl *template.Template + *sql.DB + UserDB *sql.DB + *Conf OAuthConf map[string]*oauth2.Config Cookies *sessions.CookieStore } +// UserAccount is an user account +type UserAccount struct { + ID string // UUID + DisplayName string +} + // Series is a book series. type Series struct { ID int64 `json:"id,omitempty"` diff --git a/bouquins/db.go b/bouquins/db.go index dce7b4a..bcf6b4b 100644 --- a/bouquins/db.go +++ b/bouquins/db.go @@ -104,6 +104,8 @@ const ( AND authors.id != ? ORDER BY authors.id` sqlAuthor = "SELECT name FROM authors WHERE id = ?" + sqlAccount = "SELECT accounts.id, name FROM accounts, authentifiers WHERE authentifiers.id = accounts.id AND authentifiers.authentifier = ?" + defaultLimit = 10 qtBook QueryType = iota @@ -162,7 +164,10 @@ var queries = map[Query]string{ Query{qtAuthorBooks, false, false}: sqlAuthorBooks, Query{qtAuthorCoauthors, false, false}: sqlAuthorAuthors, } -var stmts = make(map[Query]*sql.Stmt) +var ( + stmts = make(map[Query]*sql.Stmt) + stmtAccount *sql.Stmt +) // QueryType is a type of query, with variants for sort and order type QueryType uint @@ -214,6 +219,13 @@ func (app *Bouquins) PrepareAll() error { } stmts[q] = stmt } + // users.db + var err error + stmtAccount, err = app.UserDB.Prepare(sqlAccount) + if err != nil { + log.Println(err, sqlAccount) + errcount++ + } if errcount > 0 { return fmt.Errorf("%d errors on queries, see logs", errcount) } diff --git a/bouquins/dbusers.go b/bouquins/dbusers.go new file mode 100644 index 0000000..3631b0d --- /dev/null +++ b/bouquins/dbusers.go @@ -0,0 +1,11 @@ +package bouquins + +// Account returns user account from authentifier +func Account(authentifier string) (*UserAccount, error) { + account := new(UserAccount) + err := stmtAccount.QueryRow(authentifier).Scan(&account.ID, &account.DisplayName) + if err != nil { + return nil, err + } + return account, nil +} diff --git a/main.go b/main.go index 14048e7..39fc05a 100644 --- a/main.go +++ b/main.go @@ -15,8 +15,6 @@ import ( "meutel.net/meutel/go-bouquins/bouquins" ) -var db *sql.DB - // ReadConfig loads configuration file and initialize default value func ReadConfig() (*bouquins.Conf, error) { conf := new(bouquins.Conf) @@ -39,13 +37,16 @@ func ReadConfig() (*bouquins.Conf, error) { if conf.DbPath == "" { conf.DbPath = conf.CalibrePath + "/metadata.db" } + if conf.UserDbPath == "" { + conf.UserDbPath = "./users.db" + } if conf.BindAddress == "" { conf.BindAddress = ":9000" } return conf, err } -func initApp() *bouquins.Conf { +func initApp() *bouquins.Bouquins { log.SetFlags(log.LstdFlags | log.Lshortfile) conf, err := ReadConfig() if err != nil { @@ -56,23 +57,33 @@ func initApp() *bouquins.Conf { if err != nil { log.Fatalln(err) } - db, err = sql.Open("sqlite3", conf.DbPath) + db, err := sql.Open("sqlite3", conf.DbPath) + if err != nil { + log.Fatalln(err) + } + userdb, err := sql.Open("sqlite3", conf.UserDbPath) if err != nil { log.Fatalln(err) } - oauthConf := make(map[string]*oauth2.Config) - for _, provider := range bouquins.Providers { - oauthConf[provider.Name()] = provider.Config(conf) + app := &bouquins.Bouquins{ + Tpl: tpl, + DB: db, + UserDB: userdb, + Conf: conf, + OAuthConf: make(map[string]*oauth2.Config), + Cookies: sessions.NewCookieStore([]byte(conf.CookieSecret)), + } + for _, provider := range bouquins.Providers { + app.OAuthConf[provider.Name()] = provider.Config(conf) } - app := &bouquins.Bouquins{Tpl: tpl, DB: db, OAuthConf: oauthConf, Cookies: sessions.NewCookieStore([]byte(conf.CookieSecret))} err = app.PrepareAll() if err != nil { log.Fatalln(err) } assets(conf.CalibrePath) router(app) - return conf + return app } func assets(calibre string) { @@ -109,7 +120,8 @@ func router(app *bouquins.Bouquins) { } func main() { - conf := initApp() - defer db.Close() - http.ListenAndServe(conf.BindAddress, nil) + app := initApp() + defer app.DB.Close() + defer app.UserDB.Close() + http.ListenAndServe(app.Conf.BindAddress, nil) }