branch bow-v2-go updated (a19f7b9 -> 0c81e25)
This is an automated email from the git hooks/post-receive script. New change to branch bow-v2-go in repository bow. See https://gitlab.nuiton.org/chorem/bow.git from a19f7b9 ajout des policy (ajout d'un utilisateur nobody) debut gestion des erreurs new 68f57ba en cours de refactoring des requetes sql new 0c81e25 factorisation des appels sql The 2 revisions listed above as "new" are entirely new to this repository and will be described in separate emails. The revisions listed as "adds" were already present in the repository and have only been added to this reference. Detailed log of new commits: commit 0c81e25b8982d895573583aa39b46fdae38a017b Author: Benjamin <poussin@codelutin.com> Date: Fri Apr 10 00:53:27 2020 +0200 factorisation des appels sql commit 68f57ba40a7af7c8c6c5476b1ff0d289b7af55e9 Author: Benjamin <poussin@codelutin.com> Date: Thu Apr 9 22:56:42 2020 +0200 en cours de refactoring des requetes sql Summary of changes: cmd/bow/main.go | 2 +- doc/implementation.md | 1 + migrate/001_init_schema.sql | 56 ++++++++++--- pkg/http/userResource.go | 46 +++++++--- pkg/repository/bookmarkRepository.go | 24 +++++- pkg/repository/database.go | 74 +++++++++++++++- pkg/repository/userRepository.go | 153 +++++++++++++++------------------- pkg/repository/userRepository_test.go | 20 +++++ pkg/utils/error.go | 57 +++++++++++++ 9 files changed, 317 insertions(+), 116 deletions(-) create mode 100644 pkg/repository/userRepository_test.go create mode 100644 pkg/utils/error.go -- To stop receiving notification emails like this one, please contact chorem.org SCM administrator <admin+scm@chorem.org>.
This is an automated email from the git hooks/post-receive script. New commit to branch bow-v2-go in repository bow. See https://gitlab.nuiton.org/chorem/bow.git commit 68f57ba40a7af7c8c6c5476b1ff0d289b7af55e9 Author: Benjamin <poussin@codelutin.com> Date: Thu Apr 9 22:56:42 2020 +0200 en cours de refactoring des requetes sql --- doc/implementation.md | 1 + migrate/001_init_schema.sql | 50 ++++++++-- pkg/repository/database.go | 46 +++++++++ pkg/repository/userRepository.go | 211 ++++++++++++++++++++++++++++++++------- pkg/utils/error.go | 52 ++++++++++ 5 files changed, 312 insertions(+), 48 deletions(-) diff --git a/doc/implementation.md b/doc/implementation.md index b8f3fcb..fbbe2e9 100644 --- a/doc/implementation.md +++ b/doc/implementation.md @@ -1,4 +1,5 @@ TODO: table d'historique d'authentification +TODO: faire des tests de perf entre TEXT[] et jsonb qui stockerait que des chaines == Creation d'un compte diff --git a/migrate/001_init_schema.sql b/migrate/001_init_schema.sql index 5ca5dcd..a40815a 100644 --- a/migrate/001_init_schema.sql +++ b/migrate/001_init_schema.sql @@ -8,7 +8,7 @@ CREATE EXTENSION IF NOT EXISTS "citext"; -- CREATE EXTENSION IF NOT EXISTS "btree_gist"; -- fonction a utilise pour les recherches dans les tableaux de text (ex: where text(tags) ilike '%adm%') -CREATE OR REPLACE FUNCTION text(citext[]) +CREATE OR REPLACE FUNCTION text(text[]) returns text language sql immutable as $$ select $1::text $$; @@ -63,8 +63,8 @@ CREATE TABLE bowgroup ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), creationDate timestamp DEFAULT current_timestamp, updateDate timestamp DEFAULT current_timestamp, - name Text, - password Text, + name TEXT, + description TEXT, tokens jsonb, -- [{name: sring, token: string, expiration: date}] admin UUID[], writer UUID[], @@ -82,7 +82,7 @@ CREATE TABLE bookmark ( owner UUID REFERENCES bowUser(id) ON DELETE CASCADE ON UPDATE CASCADE, uri TEXT, description TEXT, - tags citext[], + tags text[], creationDate TIMESTAMP, updateDate timestamp DEFAULT current_timestamp, importDate TIMESTAMP, @@ -158,28 +158,60 @@ CREATE TRIGGER update_Bookmark_updateDate BEFORE INSERT OR UPDATE ON Bookmark FO -- nobody n'herite pas des droits des autres pour le force a faire un "set role" CREATE USER nobody WITH NOINHERIT CREATEROLE LOGIN PASSWORD '{{.nobody_password}}' +-- l'utilisateur nobody a le droit d'inserer des users (creation de compte) et c'est lui qui visite les pages GRANT INSERT ON bowUser TO nobody; GRANT INSERT ON pageHistory TO nobody; -GRANT SELECT, DELETE, TRIGGER ON bowUser TO PUBLIC; +-- tout le monde a le droit de faire certaine chose, on restraint les updates au champs qui peuvent varier +GRANT SELECT, INSERT, DELETE, TRIGGER ON bowUser TO PUBLIC; GRANT UPDATE (updateDate, password, tokens, emails, unconfirmedEmails, authenticationInfo, autoScreenshot, autoFavicon, maxTagInCloud, maxResult, actions) ON bowUser TO PUBLIC; +GRANT SELECT, INSERT, DELETE, TRIGGER ON bowgroup TO PUBLIC; +GRANT UPDATE(updateDate, description, tokens, admin, writer, reader) ON bowgroup TO PUBLIC; GRANT SELECT, INSERT, DELETE, TRIGGER ON bookmark TO PUBLIC; GRANT UPDATE(uri, description, tags, updateDate, privateAlias, publicAlias, authenticationInfo, favicon, screenshot, visit, lang) ON bookmark TO PUBLIC; +-- on ne peut pas modifier une action, mais on peut la supprimer GRANT SELECT, INSERT, DELETE, TRIGGER ON actionHistory TO PUBLIC; +-- on ne peut que lire les historiques de pages GRANT SELECT ON pageHistory TO PUBLIC; ALTER TABLE bowUser ENABLE ROW LEVEL SECURITY; +ALTER TABLE bowgroup ENABLE ROW LEVEL SECURITY; ALTER TABLE bookmark ENABLE ROW LEVEL SECURITY; ALTER TABLE actionHistory ENABLE ROW LEVEL SECURITY; +-- les utilisateurs n'ont le droit d'agir que sur leur propre compte CREATE POLICY bowUser_access ON bowUser - USING (id::text = current_user); + FOR ALL + USING (id = current_user::uuid); + +-- tout le monde peut lire les groupes si on est dans les utilisateurs du groupes +CREATE POLICY bowgroup_access_select ON bowgroup + FOR SELECT + USING ((admin || writer || reader ) @> ('{' || current_user || '}')::uuid[]); + +-- tous les utilisateurs peuvent créer un groupe s'il finisse admin du groupe +CREATE POLICY bowgroup_access_insert ON bowgroup + FOR INSERT + WITH CHECK (admin @> ('{' || current_user || '}')::uuid[]); +-- seul les admins peuvent ajouter/retirer des utilisateurs d'un group, mais ils ne peuvent pas se retirer eux meme de l'admin (il reste toujours un admin) +CREATE POLICY bowgroup_access_update ON bowgroup + FOR UPDATE + USING (admin @> ('{' || current_user || '}')::uuid[]); + +-- tout le monde peut creer des bookmarks, mais a la fin le createur doit etre owner CREATE POLICY bookmark_access ON bookmark - USING (owner::text = current_user); + USING (owner = current_user::uuid); + +-- si un bookmark a comme tag '@toto' et proprietaire '1234' alors il est visible de toutes les utilisateurs +-- appartenant (admin, writer, reader) au groupe 'toto' dont l'utilisateur '1234' est admin ou writer +CREATE POLICY bookmark_access_group ON bookmark + USING ( current_user::uuid in (SELECT unnest(admin || writer || reader) FROM bowgroup WHERE tags @> ('{@' || name || '}')::text[] + AND ('{' || owner || '}')::uuid[] <@ (admin || writer))); +-- tout le monde peut gerer des actionHistory, mais a la fin le createur doit etre owner (il n'y pas droit au update via le GRANT) CREATE POLICY actionHistory_access ON actionHistory - USING (owner::text = current_user); + USING (owner = current_user::uuid); -- droit specifique pour nobody -- il peut lire tous les id, login, email, password @@ -200,10 +232,8 @@ DROP TYPE AuthenticationInfo; DROP TYPE Action; DROP TYPE Token; -DROP FUNCTION text(citext[]); DROP FUNCTION update_creationDate_column; DROP FUNCTION update_updateDate_column; DROP EXTENSION IF EXISTS "pgcrypto"; DROP EXTENSION IF EXISTS "pg_trgm"; -DROP EXTENSION IF EXISTS "citext"; \ No newline at end of file diff --git a/pkg/repository/database.go b/pkg/repository/database.go index 795373c..a925fa8 100644 --- a/pkg/repository/database.go +++ b/pkg/repository/database.go @@ -151,3 +151,49 @@ func migrateDatabase(databaseURL string) { os.Exit(1) } } + +func execOnOneRow(currentUserID string, presql string, sql string, postsql string, arguments ...interface{}) error { + tx, err := db.Begin(context.Background()) + if err != nil { + return utils.NewHTTPError500(err, currentUserID) + } + defer tx.Rollback(context.Background()) + + if presql != nil { + _, err = db.Exec(context.Background(), fmt.Sprintf(presql, currentUserID)) + if err != nil { + return utils.NewHTTPError500(err, currentUserID) + } + } + + _, err = db.Exec(context.Background(), fmt.Sprintf(` + SET ROLE "%[1]s"; + `, currentUserID)) + if err != nil { + return utils.NewHTTPError500(err, currentUserID) + } + + modif, err := db.Exec(context.Background(), sql, arguments) + + if err != nil { + return utils.NewHTTPError500(err, currentUserID) + } + + if modif.RowsAffected() != 1 { + return return utils.NewHTTPError(fmt.Sprintf("User not found '%s'", id), currentUserID, 404) + } + + if postsql != nil { + _, err = db.Exec(context.Background(), fmt.Sprintf(postsql, currentUserID)) + if err != nil { + return utils.NewHTTPError500(err, currentUserID) + } + } + + err = tx.Commit(context.Background()) + if err != nil { + return utils.NewHTTPError500(err, currentUserID) + } + + return nil +} diff --git a/pkg/repository/userRepository.go b/pkg/repository/userRepository.go index e630963..20c234d 100644 --- a/pkg/repository/userRepository.go +++ b/pkg/repository/userRepository.go @@ -98,7 +98,9 @@ func CheckUserPasswordForEmail(loginOrEmail string, password string) (string, er var hash string row := db.QueryRow(context.Background(), `select id, password from bowuser where login=$1 or emails @> $2::text[]`, loginOrEmail, fmt.Sprintf(`{"%s"}`, loginOrEmail)) err := row.Scan(&uuid, &hash) - if err != nil { + if err != nil {CREATE POLICY bowUser_access ON bowUser + FOR ALL + USING (id = current_user::uuid); return "", err } @@ -121,80 +123,143 @@ CreateUser retourne l'utilisateur au format json func CreateUser(login string, password string) (string, error) { hashPassword, err := utils.HashPassword(password) if err != nil { - return "", err + return "",utils.NewHTTPError500(err, login) } uuid, err := utils.GenUUID() if err != nil { - return "", err + return "",utils.NewHTTPError500(err, login) } + currentUserID := uuid + user := model.BowUser{ ID: uuid, Login: login, Password: hashPassword, MaxTagInCloud: 20, MaxResult: 20, AutoFavicon: false, AutoScreenshot: false, AuthenticationInfo: model.AuthenticationInfo{DomainComponent: 2, MaxLength: 15}} userAsJSON, err := json.Marshal(user) if err != nil { - return "", err + return "", utils.NewHTTPError500(err, currentUserID) } log.Println("create user", string(userAsJSON)) - modif, err := db.Exec(context.Background(), fmt.Sprintf(` - INSERT INTO bowUser AS t SELECT * FROM json_populate_record(NULL::bowUser, $1::json); + + tx, err := db.Begin(context.Background()) + if err != nil { + return "", utils.NewHTTPError500(err, currentUserID) + } + defer tx.Rollback(context.Background()) + + // pas d'argument sql donc pas de requete preparer, on peut en mettre 3 + _, err = db.Exec(context.Background(), fmt.Sprintf(` CREATE ROLE "%[1]s"; GRANT "%[1]s" TO nobody; - `, user.ID), string(userAsJSON)) + SET ROLE "%[1]s"; + `, currentUserID)) + if err != nil { + return "", utils.NewHTTPError500(err, currentUserID) + } - if modif.RowsAffected() != 1 { - return "", errors.New("No user created") + modif, err := db.Exec(context.Background(), ` + INSERT INTO bowUser AS t SELECT * FROM json_populate_record(NULL::bowUser, $1::json); + `, string(userAsJSON)) + + if err != nil || modif.RowsAffected() != 1 { + return "", utils.errors.New("No user created") + } + + err = tx.Commit(context.Background()) + if err != nil { + return "", utils.NewHTTPError500(err, currentUserID) } - return user.ID, err + return user.ID, nil } /* DeleteUser suppression d'un nouveau bookmark */ -func DeleteUser(id string) error { - modif, err := db.Exec(context.Background(), fmt.Sprintf(` - DELETE FROM bowuser WHERE id=$1; - DROP ROLE IF EXISTS %s; - `, id), id) +func DeleteUser(currentUserID string, id string) error { + tx, err := db.Begin(context.Background()) + if err != nil { + return "", utils.NewHTTPError500(err, currentUserID) + } + defer tx.Rollback(context.Background()) + + _, err = db.Exec(context.Background(), fmt.Sprintf(` + SET ROLE "%[1]s"; + `, currentUserID)) + if err != nil { + return "", utils.NewHTTPError500(err, currentUserID) + } + + modif, err := db.Exec(context.Background(), ` + DELETE FROM bowuser WHERE id=$1;`, id) if modif.RowsAffected() != 1 { return fmt.Errorf("No user found for id '%s'", id) } + _, err = db.Exec(context.Background(), fmt.Sprintf(` + RESET ROLE; + DROP ROLE IF EXISTS "%s"; + `, currentUserID)) + if err != nil { + return "", utils.NewHTTPError500(err, currentUserID) + } + + err = tx.Commit(context.Background()) + if err != nil { + return "", utils.NewHTTPError500(err, currentUserID) + } + return err } /* UpdateUserPassword update user password, if old password match, or if force is true */ -func UpdateUserPassword(id string, password string, oldPassword string, force bool) error { - if force || CheckUserPasswordForID(id, oldPassword) { +func UpdateUserPassword(currentUserID string, id string, password string, oldPassword string, force bool) error { + if !force && !CheckUserPasswordForID(id, oldPassword) { + return utils.NewHTTPError(fmt.Sprintf("Bad old password for user '%s'", id), currentUserID, 400) + } - hash, err := utils.HashPassword(password) + hash, err := utils.HashPassword(password) + if err != nil { + return utils.NewHTTPError500(err, currentUserID) + } - if err == nil { - modif, err := db.Exec(context.Background(), `update bowuser SET password=$2 where id=$1;`, id, hash) - if err != nil { - return err - } + tx, err := db.Begin(context.Background()) + if err != nil { + return utils.NewHTTPError500(err, currentUserID) + } + defer tx.Rollback(context.Background()) - if modif.RowsAffected() != 1 { - return fmt.Errorf("No user found for id '%s'", id) - } - } + _, err = db.Exec(context.Background(), fmt.Sprintf(` + SET ROLE "%[1]s"; + `, currentUserID)) + if err != nil { + return utils.NewHTTPError500(err, currentUserID) + } - return err + modif, err := db.Exec(context.Background(), `update bowuser SET password=$2 where id=$1;`, id, hash) + if err != nil { + return utils.NewHTTPError500(err, currentUserID) + } + + if modif.RowsAffected() != 1 { + return return utils.NewHTTPError(fmt.Sprintf("User not found '%s'", id), currentUserID, 404) + } + err = tx.Commit(context.Background()) + if err != nil { + return utils.NewHTTPError500(err, currentUserID) } - return fmt.Errorf("Bad old password for user '%s'", id) + return nil } /* AddUserToken ajout un tocken d'authentification pour l'utilisateur */ -func AddUserToken(id string, name string, expiration time.Time) (string, error) { +func AddUserToken(currentUserID string, id string, name string, expiration time.Time) (string, error) { token, err := utils.GenUUID() if err != nil { return "", err @@ -205,18 +270,41 @@ func AddUserToken(id string, name string, expiration time.Time) (string, error) return token, err } + tx, err := db.Begin(context.Background()) + if err != nil { + return utils.NewHTTPError500(err, currentUserID) + } + defer tx.Rollback(context.Background()) + + _, err = db.Exec(context.Background(), fmt.Sprintf(` + SET ROLE "%[1]s"; + `, currentUserID)) + if err != nil { + return utils.NewHTTPError500(err, currentUserID) + } + modif, err := db.Exec(context.Background(), `update bowuser SET tokens=coalesce(tokens, '[]'::jsonb) || $2::jsonb where id=$1;`, id, json) + + if err != nil { + return utils.NewHTTPError500(err, currentUserID) + } + if modif.RowsAffected() != 1 { - return "", fmt.Errorf("No user found for id '%s'", id) + return return utils.NewHTTPError(fmt.Sprintf("User not found '%s'", id), currentUserID, 404) + } + + err = tx.Commit(context.Background()) + if err != nil { + return utils.NewHTTPError500(err, currentUserID) } - return token, err + return token, nil } /* AddUserUnconfirmedEmail ajout d'un email non confirme, retourne le token permettant la confirmation */ -func AddUserUnconfirmedEmail(id string, email string) (string, error) { +func AddUserUnconfirmedEmail(currentUserID string, id string, email string) (string, error) { token, err := utils.GenUUID() if err != nil { return "", err @@ -227,31 +315,78 @@ func AddUserUnconfirmedEmail(id string, email string) (string, error) { return token, err } + tx, err := db.Begin(context.Background()) + if err != nil { + return utils.NewHTTPError500(err, currentUserID) + } + defer tx.Rollback(context.Background()) + + _, err = db.Exec(context.Background(), fmt.Sprintf(` + SET ROLE "%[1]s"; + `, currentUserID)) + if err != nil { + return utils.NewHTTPError500(err, currentUserID) + } + modif, err := db.Exec(context.Background(), `update bowuser SET unconfirmedemails=coalesce(unconfirmedemails, '[]'::jsonb) || $2::jsonb where id=$1;`, id, json) + + if err != nil { + return utils.NewHTTPError500(err, currentUserID) + } + if modif.RowsAffected() != 1 { - return "", fmt.Errorf("No user found for id '%s'", id) + return return utils.NewHTTPError(fmt.Sprintf("User not found '%s'", id), currentUserID, 404) + } + + err = tx.Commit(context.Background()) + if err != nil { + return utils.NewHTTPError500(err, currentUserID) } - return token, err + return token, nil } /* UpdateUserAuthenticationInfo met a jour les infos d'authentification */ -func UpdateUserAuthenticationInfo(id string, auth model.AuthenticationInfo) error { +func UpdateUserAuthenticationInfo(currentUserID string, id string, auth model.AuthenticationInfo) error { authAsJSON, err := json.Marshal(auth) if err != nil { return err } + tx, err := db.Begin(context.Background()) + if err != nil { + return utils.NewHTTPError500(err, currentUserID) + } + defer tx.Rollback(context.Background()) + + _, err = db.Exec(context.Background(), fmt.Sprintf(` + SET ROLE "%[1]s"; + `, currentUserID)) + if err != nil { + return utils.NewHTTPError500(err, currentUserID) + } + modif, err := db.Exec(context.Background(), ` UPDATE bowuser SET (authenticationinfo) = (SELECT * FROM json_populate_record(NULL::authenticationinfo, $2::json)) WHERE id=$1`, id, string(authAsJSON)) + + if err != nil { + return utils.NewHTTPError500(err, currentUserID) + } + if modif.RowsAffected() != 1 { - return fmt.Errorf("No user found for id '%s'", id) + return return utils.NewHTTPError(fmt.Sprintf("User not found '%s'", id), currentUserID, 404) } - return err + + err = tx.Commit(context.Background()) + if err != nil { + return utils.NewHTTPError500(err, currentUserID) + } + + return nil } /* diff --git a/pkg/utils/error.go b/pkg/utils/error.go new file mode 100644 index 0000000..1c7afe3 --- /dev/null +++ b/pkg/utils/error.go @@ -0,0 +1,52 @@ +package utils + +import ( + "fmt" + "net/http" +) + +/* +httpError erreur permettant de porter le code HTTP souhaite +*/ +type httpError struct { + ID string + Msg string + CurrentUserID string + StatusCode int +} + +func (e *httpError) Error() string { + return fmt.Sprintf(`{"ID": "%s", "CurrentUserID": "%s", "StatusCode": %v, "Msg": %q}`, e.ID, e.CurrentUserID, e.StatusCode, e.Msg) +} + +func NewHTTPError(msg string, currentUserID string, statusCode int) *httpError { + e := httpError{} + e.ID, _ = GenUUID() + e.Msg = msg + e.CurrentUserID = currentUserID + if statusCode > 0 { + e.StatusCode = statusCode + } else { + e.StatusCode = 500 + } + + return &e +} + +func NewHTTPError500(err error, currentUserID string) *httpError { + e := httpError{} + e.ID, _ = GenUUID() + e.Msg = fmt.Sprintf("%v", err) + e.CurrentUserID = currentUserID + e.StatusCode = 500 + + return &e +} + +func Throw(w http.ResponseWriter, err error) { + if err, ok := err.(*httpError); ok { + http.Error(w, fmt.Sprintf("%s", err), err.StatusCode) + } else { + http.Error(w, fmt.Sprintf("%s", err), 500) + } +} -- To stop receiving notification emails like this one, please contact chorem.org SCM administrator <admin+scm@chorem.org>.
This is an automated email from the git hooks/post-receive script. New commit to branch bow-v2-go in repository bow. See https://gitlab.nuiton.org/chorem/bow.git commit 0c81e25b8982d895573583aa39b46fdae38a017b Author: Benjamin <poussin@codelutin.com> Date: Fri Apr 10 00:53:27 2020 +0200 factorisation des appels sql --- cmd/bow/main.go | 2 +- migrate/001_init_schema.sql | 10 +- pkg/http/userResource.go | 46 ++++-- pkg/repository/bookmarkRepository.go | 24 +++- pkg/repository/database.go | 52 +++++-- pkg/repository/userRepository.go | 254 +++++++--------------------------- pkg/repository/userRepository_test.go | 20 +++ pkg/utils/error.go | 5 + 8 files changed, 175 insertions(+), 238 deletions(-) diff --git a/cmd/bow/main.go b/cmd/bow/main.go index 6a6fc9f..2edef6c 100644 --- a/cmd/bow/main.go +++ b/cmd/bow/main.go @@ -11,7 +11,7 @@ import ( func main() { databaseURL := os.Getenv("DATABASE_URL") log.Println("Init database") - repository.Init(databaseURL) + repository.Init(databaseURL, true) // u := model.BowUser{ID: utils.GenUUID(), Login: "bpoussin", Emails: []string{"benjamin@pouss.in"}, Password: "toto"} diff --git a/migrate/001_init_schema.sql b/migrate/001_init_schema.sql index a40815a..415c082 100644 --- a/migrate/001_init_schema.sql +++ b/migrate/001_init_schema.sql @@ -163,7 +163,7 @@ GRANT INSERT ON bowUser TO nobody; GRANT INSERT ON pageHistory TO nobody; -- tout le monde a le droit de faire certaine chose, on restraint les updates au champs qui peuvent varier -GRANT SELECT, INSERT, DELETE, TRIGGER ON bowUser TO PUBLIC; +GRANT SELECT, DELETE, TRIGGER ON bowUser TO PUBLIC; GRANT UPDATE (updateDate, password, tokens, emails, unconfirmedEmails, authenticationInfo, autoScreenshot, autoFavicon, maxTagInCloud, maxResult, actions) ON bowUser TO PUBLIC; GRANT SELECT, INSERT, DELETE, TRIGGER ON bowgroup TO PUBLIC; GRANT UPDATE(updateDate, description, tokens, admin, writer, reader) ON bowgroup TO PUBLIC; @@ -216,10 +216,16 @@ CREATE POLICY actionHistory_access ON actionHistory -- droit specifique pour nobody -- il peut lire tous les id, login, email, password -- il peut creer de nouveau utilisateur -CREATE POLICY bowUser_nobody_access ON bowUser TO nobody +CREATE POLICY bowUser_nobody_access ON bowUser FOR SELECT + TO nobody USING (true); +CREATE POLICY bowUser_nobody_insert ON bowUser + FOR INSERT + TO nobody + WITH CHECK (true); + ---- create above / drop below ---- DROP TABLE actionHistory; diff --git a/pkg/http/userResource.go b/pkg/http/userResource.go index bf60624..758f09b 100644 --- a/pkg/http/userResource.go +++ b/pkg/http/userResource.go @@ -92,7 +92,7 @@ func createUser(w http.ResponseWriter, r *http.Request) { id, err := repository.CreateUser(data["login"], data["password"]) if err != nil { - http.Error(w, fmt.Sprintf("%s", err), 400) + utils.Throw(w, err) return } @@ -100,10 +100,12 @@ func createUser(w http.ResponseWriter, r *http.Request) { } func deleteUser(w http.ResponseWriter, r *http.Request) { + currentUserID := r.Context().Value(utils.UserID).(string) + id := mux.Vars(r)["id"] log.Println("deleteUser", id) - err := repository.DeleteUser(id) + err := repository.DeleteUser(currentUserID, id) if err != nil { http.Error(w, fmt.Sprintf("%s", err), 400) return @@ -115,6 +117,8 @@ updateUserPassword body: {"password": "xxxx", "oldPassword": "yyyy"} */ func updateUserPassword(w http.ResponseWriter, r *http.Request) { + currentUserID := r.Context().Value(utils.UserID).(string) + id := mux.Vars(r)["id"] log.Println("updateUserPassword", id) @@ -125,7 +129,7 @@ func updateUserPassword(w http.ResponseWriter, r *http.Request) { return } - err = repository.UpdateUserPassword(id, data["password"], data["oldPassword"], false) + err = repository.UpdateUserPassword(currentUserID, id, data["password"], data["oldPassword"], false) if err != nil { http.Error(w, fmt.Sprintf("%s", err), 400) return @@ -138,6 +142,8 @@ body: {"name": "for application toto", "expiration": 1586081695000} return: {"token": "uuid"} */ func addUserToken(w http.ResponseWriter, r *http.Request) { + currentUserID := r.Context().Value(utils.UserID).(string) + id := mux.Vars(r)["id"] var data model.Token @@ -149,7 +155,7 @@ func addUserToken(w http.ResponseWriter, r *http.Request) { log.Println("addUserToken", id, data.Name, data.Expiration) - token, err := repository.AddUserToken(id, data.Name, data.Expiration) + token, err := repository.AddUserToken(currentUserID, id, data.Name, data.Expiration) if err != nil { http.Error(w, fmt.Sprintf("%s", err), 400) return @@ -164,6 +170,8 @@ body: {"name": "for application toto", "expiration": 1586081695000} return: {"token": "uuid"} */ func addUserUnconfirmedEmail(w http.ResponseWriter, r *http.Request) { + currentUserID := r.Context().Value(utils.UserID).(string) + id := mux.Vars(r)["id"] var data model.UnconfirmedEmails @@ -175,7 +183,7 @@ func addUserUnconfirmedEmail(w http.ResponseWriter, r *http.Request) { log.Println("addUserUnconfirmedEmail", id) - token, err := repository.AddUserUnconfirmedEmail(id, data.Email) + token, err := repository.AddUserUnconfirmedEmail(currentUserID, id, data.Email) if err != nil { http.Error(w, fmt.Sprintf("%s", err), 400) return @@ -187,6 +195,8 @@ func addUserUnconfirmedEmail(w http.ResponseWriter, r *http.Request) { } func updateUserAuthenticationInfo(w http.ResponseWriter, r *http.Request) { + currentUserID := r.Context().Value(utils.UserID).(string) + id := mux.Vars(r)["id"] var auth model.AuthenticationInfo err := json.NewDecoder(r.Body).Decode(&auth) @@ -197,7 +207,7 @@ func updateUserAuthenticationInfo(w http.ResponseWriter, r *http.Request) { log.Println("updateUserAuthenticationInfo", id, auth) - err = repository.UpdateUserAuthenticationInfo(id, auth) + err = repository.UpdateUserAuthenticationInfo(currentUserID, id, auth) if err != nil { http.Error(w, fmt.Sprintf("%s", err), 400) return @@ -205,6 +215,8 @@ func updateUserAuthenticationInfo(w http.ResponseWriter, r *http.Request) { } func updateUserActions(w http.ResponseWriter, r *http.Request) { + currentUserID := r.Context().Value(utils.UserID).(string) + id := mux.Vars(r)["id"] var actions []model.Action err := json.NewDecoder(r.Body).Decode(&actions) @@ -215,7 +227,7 @@ func updateUserActions(w http.ResponseWriter, r *http.Request) { log.Println("updateUserActions", id, actions) - err = repository.UpdateUserActions(id, actions) + err = repository.UpdateUserActions(currentUserID, id, actions) if err != nil { http.Error(w, fmt.Sprintf("%s", err), 400) return @@ -223,6 +235,8 @@ func updateUserActions(w http.ResponseWriter, r *http.Request) { } func updateUserAutoScreenshot(w http.ResponseWriter, r *http.Request) { + currentUserID := r.Context().Value(utils.UserID).(string) + id := mux.Vars(r)["id"] var data map[string]bool @@ -234,7 +248,7 @@ func updateUserAutoScreenshot(w http.ResponseWriter, r *http.Request) { log.Println("updateUserAutoScreenshot", id, data) - err = repository.UpdateUserAutoScreenshot(id, data["autoscreenshot"]) + err = repository.UpdateUserAutoScreenshot(currentUserID, id, data["autoscreenshot"]) if err != nil { http.Error(w, fmt.Sprintf("%s", err), 400) return @@ -242,6 +256,8 @@ func updateUserAutoScreenshot(w http.ResponseWriter, r *http.Request) { } func updateUserAutoFavicon(w http.ResponseWriter, r *http.Request) { + currentUserID := r.Context().Value(utils.UserID).(string) + id := mux.Vars(r)["id"] var data map[string]bool @@ -253,7 +269,7 @@ func updateUserAutoFavicon(w http.ResponseWriter, r *http.Request) { log.Println("updateUserAutoFavicon", id, data) - err = repository.UpdateUserAutoFavicon(id, data["autofavicon"]) + err = repository.UpdateUserAutoFavicon(currentUserID, id, data["autofavicon"]) if err != nil { http.Error(w, fmt.Sprintf("%s", err), 400) return @@ -261,6 +277,8 @@ func updateUserAutoFavicon(w http.ResponseWriter, r *http.Request) { } func updateUserMaxTagInCloud(w http.ResponseWriter, r *http.Request) { + currentUserID := r.Context().Value(utils.UserID).(string) + id := mux.Vars(r)["id"] var data map[string]int8 @@ -272,7 +290,7 @@ func updateUserMaxTagInCloud(w http.ResponseWriter, r *http.Request) { log.Println("updateUserMaxTagInCloud", id, data) - err = repository.UpdateUserMaxTagInCloud(id, data["maxtagincloud"]) + err = repository.UpdateUserMaxTagInCloud(currentUserID, id, data["maxtagincloud"]) if err != nil { http.Error(w, fmt.Sprintf("%s", err), 400) return @@ -280,6 +298,8 @@ func updateUserMaxTagInCloud(w http.ResponseWriter, r *http.Request) { } func updateUserMaxResult(w http.ResponseWriter, r *http.Request) { + currentUserID := r.Context().Value(utils.UserID).(string) + id := mux.Vars(r)["id"] var data map[string]int8 @@ -291,7 +311,7 @@ func updateUserMaxResult(w http.ResponseWriter, r *http.Request) { log.Println("updateUserMaxResult", id, data) - err = repository.UpdateUserMaxResult(id, data["maxresult"]) + err = repository.UpdateUserMaxResult(currentUserID, id, data["maxresult"]) if err != nil { http.Error(w, fmt.Sprintf("%s", err), 400) return @@ -299,10 +319,12 @@ func updateUserMaxResult(w http.ResponseWriter, r *http.Request) { } func confirmUserEmail(w http.ResponseWriter, r *http.Request) { + currentUserID := r.Context().Value(utils.UserID).(string) + id := mux.Vars(r)["id"] token := mux.Vars(r)["token"] - err := repository.ConfirmUserEmail(id, token) + err := repository.ConfirmUserEmail(currentUserID, id, token) if err != nil { http.Error(w, fmt.Sprintf("%s", err), 400) return diff --git a/pkg/repository/bookmarkRepository.go b/pkg/repository/bookmarkRepository.go index f90ac04..39c72ff 100644 --- a/pkg/repository/bookmarkRepository.go +++ b/pkg/repository/bookmarkRepository.go @@ -12,19 +12,37 @@ import ( /* BookmarkJSON retourne le bookmark au format json */ -func BookmarkJSON(id string) (string, error) { +func BookmarkJSON(currentUserID string, id string) (string, error) { + tx, err := db.Begin(context.Background()) + if err != nil { + return "", utils.NewHTTPError500(err, currentUserID) + } + defer tx.Rollback(context.Background()) + _, err = db.Exec(context.Background(), fmt.Sprintf(`SET ROLE "%[1]s";`, currentUserID)) + if err != nil { + return "", utils.NewHTTPError500(err, currentUserID) + } + var pgjson pgtype.JSON row := db.QueryRow(context.Background(), `WITH __all AS (select * from bookmark where id=$1) SELECT json_agg(__all.*) as j FROM __all`, id) err := row.Scan(&pgjson) if err != nil { - return "", err + return "", utils.NewHTTPError500(err, currentUserID) + } + + err = tx.Commit(context.Background()) + if err != nil { + return "", utils.NewHTTPError500(err, currentUserID) } var result string err = pgjson.AssignTo(&result) + if err != nil { + return "", utils.NewHTTPError500(err, currentUserID) + } - return result, err + return result, nil } /* diff --git a/pkg/repository/database.go b/pkg/repository/database.go index a925fa8..b029559 100644 --- a/pkg/repository/database.go +++ b/pkg/repository/database.go @@ -12,6 +12,7 @@ import ( "github.com/jackc/pgx/v4" "github.com/jackc/pgx/v4/pgxpool" "github.com/jackc/tern/migrate" + "gitlab.chorem.org/chorem/bow/pkg/utils" ) var db *pgxpool.Pool @@ -26,8 +27,10 @@ type errorLineExtract struct { /* Init initialise la connexion a la base en utilisant */ -func Init(databaseURL string) { - migrateDatabase(databaseURL) +func Init(databaseURL string, doMigration bool) { + if (doMigration) { + migrateDatabase(databaseURL) + } poolConfig, err := pgxpool.ParseConfig(databaseURL) if err != nil { @@ -152,39 +155,60 @@ func migrateDatabase(databaseURL string) { } } -func execOnOneRow(currentUserID string, presql string, sql string, postsql string, arguments ...interface{}) error { +type query struct { + asNobody bool + presql string + sql string + postsql string +} + +func (q *query)setPreSQL(sql string, arguments ...interface{}) { + q.presql = fmt.Sprintf(sql, arguments...) +} + +func (q *query)setSQL(sql string, arguments ...interface{}) { + q.sql = fmt.Sprintf(sql, arguments...) +} + +func (q *query)setPostSQL(sql string, arguments ...interface{}) { + q.postsql = fmt.Sprintf(sql, arguments...) +} + +func (q *query)execOnOneRow(currentUserID string, arguments ...interface{}) error { tx, err := db.Begin(context.Background()) if err != nil { return utils.NewHTTPError500(err, currentUserID) } defer tx.Rollback(context.Background()) - if presql != nil { - _, err = db.Exec(context.Background(), fmt.Sprintf(presql, currentUserID)) + if q.presql != "" { + _, err = db.Exec(context.Background(), q.presql, pgx.QuerySimpleProtocol(true)) if err != nil { return utils.NewHTTPError500(err, currentUserID) } } - _, err = db.Exec(context.Background(), fmt.Sprintf(` - SET ROLE "%[1]s"; - `, currentUserID)) - if err != nil { - return utils.NewHTTPError500(err, currentUserID) + if !q.asNobody { + _, err = db.Exec(context.Background(), fmt.Sprintf(` + SET ROLE "%[1]s"; + `, currentUserID)) + if err != nil { + return utils.NewHTTPError500(err, currentUserID) + } } - modif, err := db.Exec(context.Background(), sql, arguments) + modif, err := db.Exec(context.Background(), q.sql, arguments...) if err != nil { return utils.NewHTTPError500(err, currentUserID) } if modif.RowsAffected() != 1 { - return return utils.NewHTTPError(fmt.Sprintf("User not found '%s'", id), currentUserID, 404) + return utils.NewHTTPError(fmt.Sprintf("User not found '%v'", arguments), currentUserID, 404) } - if postsql != nil { - _, err = db.Exec(context.Background(), fmt.Sprintf(postsql, currentUserID)) + if q.postsql != "" { + _, err = db.Exec(context.Background(), q.postsql, pgx.QuerySimpleProtocol(true)) if err != nil { return utils.NewHTTPError500(err, currentUserID) } diff --git a/pkg/repository/userRepository.go b/pkg/repository/userRepository.go index 20c234d..f5db52f 100644 --- a/pkg/repository/userRepository.go +++ b/pkg/repository/userRepository.go @@ -3,7 +3,6 @@ package repository import ( "context" "encoding/json" - "errors" "fmt" "log" "time" @@ -98,14 +97,12 @@ func CheckUserPasswordForEmail(loginOrEmail string, password string) (string, er var hash string row := db.QueryRow(context.Background(), `select id, password from bowuser where login=$1 or emails @> $2::text[]`, loginOrEmail, fmt.Sprintf(`{"%s"}`, loginOrEmail)) err := row.Scan(&uuid, &hash) - if err != nil {CREATE POLICY bowUser_access ON bowUser - FOR ALL - USING (id = current_user::uuid); + if err != nil { return "", err } if !utils.CheckPassword(password, hash) { - return "", fmt.Errorf("Password mismatch for %s", loginOrEmail) + return "", utils.NewHTTPError(fmt.Sprintf("Password mismatch for %s", loginOrEmail), "nobody", 401) } var result string @@ -128,7 +125,7 @@ func CreateUser(login string, password string) (string, error) { uuid, err := utils.GenUUID() if err != nil { - return "",utils.NewHTTPError500(err, login) + return "", utils.NewHTTPError500(err, login) } currentUserID := uuid @@ -142,74 +139,29 @@ func CreateUser(login string, password string) (string, error) { log.Println("create user", string(userAsJSON)) - tx, err := db.Begin(context.Background()) - if err != nil { - return "", utils.NewHTTPError500(err, currentUserID) - } - defer tx.Rollback(context.Background()) - - // pas d'argument sql donc pas de requete preparer, on peut en mettre 3 - _, err = db.Exec(context.Background(), fmt.Sprintf(` + q := &query{asNobody: true, sql: `INSERT INTO bowUser AS t SELECT * FROM json_populate_record(NULL::bowUser, $1::json);`} + q.setPostSQL(` CREATE ROLE "%[1]s"; GRANT "%[1]s" TO nobody; - SET ROLE "%[1]s"; - `, currentUserID)) - if err != nil { - return "", utils.NewHTTPError500(err, currentUserID) - } - - modif, err := db.Exec(context.Background(), ` - INSERT INTO bowUser AS t SELECT * FROM json_populate_record(NULL::bowUser, $1::json); - `, string(userAsJSON)) - - if err != nil || modif.RowsAffected() != 1 { - return "", utils.errors.New("No user created") - } + `, currentUserID) - err = tx.Commit(context.Background()) - if err != nil { - return "", utils.NewHTTPError500(err, currentUserID) - } + err = q.execOnOneRow(currentUserID, userAsJSON) - return user.ID, nil + return user.ID, err } /* DeleteUser suppression d'un nouveau bookmark */ func DeleteUser(currentUserID string, id string) error { - tx, err := db.Begin(context.Background()) - if err != nil { - return "", utils.NewHTTPError500(err, currentUserID) - } - defer tx.Rollback(context.Background()) - - _, err = db.Exec(context.Background(), fmt.Sprintf(` - SET ROLE "%[1]s"; - `, currentUserID)) - if err != nil { - return "", utils.NewHTTPError500(err, currentUserID) - } - - modif, err := db.Exec(context.Background(), ` - DELETE FROM bowuser WHERE id=$1;`, id) - if modif.RowsAffected() != 1 { - return fmt.Errorf("No user found for id '%s'", id) - } - - _, err = db.Exec(context.Background(), fmt.Sprintf(` + q := &query{sql: `DELETE FROM bowuser WHERE id=$1;`} + q.setPostSQL(` RESET ROLE; DROP ROLE IF EXISTS "%s"; - `, currentUserID)) - if err != nil { - return "", utils.NewHTTPError500(err, currentUserID) - } - - err = tx.Commit(context.Background()) - if err != nil { - return "", utils.NewHTTPError500(err, currentUserID) - } - + `, currentUserID) + + err := q.execOnOneRow(currentUserID, id) + return err } @@ -226,34 +178,10 @@ func UpdateUserPassword(currentUserID string, id string, password string, oldPas return utils.NewHTTPError500(err, currentUserID) } - tx, err := db.Begin(context.Background()) - if err != nil { - return utils.NewHTTPError500(err, currentUserID) - } - defer tx.Rollback(context.Background()) - - _, err = db.Exec(context.Background(), fmt.Sprintf(` - SET ROLE "%[1]s"; - `, currentUserID)) - if err != nil { - return utils.NewHTTPError500(err, currentUserID) - } + q := &query{sql: `update bowuser SET password=$2 where id=$1`} + err = q.execOnOneRow(currentUserID, id, hash) - modif, err := db.Exec(context.Background(), `update bowuser SET password=$2 where id=$1;`, id, hash) - if err != nil { - return utils.NewHTTPError500(err, currentUserID) - } - - if modif.RowsAffected() != 1 { - return return utils.NewHTTPError(fmt.Sprintf("User not found '%s'", id), currentUserID, 404) - } - - err = tx.Commit(context.Background()) - if err != nil { - return utils.NewHTTPError500(err, currentUserID) - } - - return nil + return err } /* @@ -270,35 +198,10 @@ func AddUserToken(currentUserID string, id string, name string, expiration time. return token, err } - tx, err := db.Begin(context.Background()) - if err != nil { - return utils.NewHTTPError500(err, currentUserID) - } - defer tx.Rollback(context.Background()) - - _, err = db.Exec(context.Background(), fmt.Sprintf(` - SET ROLE "%[1]s"; - `, currentUserID)) - if err != nil { - return utils.NewHTTPError500(err, currentUserID) - } - - modif, err := db.Exec(context.Background(), `update bowuser SET tokens=coalesce(tokens, '[]'::jsonb) || $2::jsonb where id=$1;`, id, json) - - if err != nil { - return utils.NewHTTPError500(err, currentUserID) - } - - if modif.RowsAffected() != 1 { - return return utils.NewHTTPError(fmt.Sprintf("User not found '%s'", id), currentUserID, 404) - } + q := &query{sql: `update bowuser SET tokens=coalesce(tokens, '[]'::jsonb) || $2::jsonb where id=$1;`} + err = q.execOnOneRow(currentUserID, id, json) - err = tx.Commit(context.Background()) - if err != nil { - return utils.NewHTTPError500(err, currentUserID) - } - - return token, nil + return token, err } /* @@ -315,33 +218,8 @@ func AddUserUnconfirmedEmail(currentUserID string, id string, email string) (str return token, err } - tx, err := db.Begin(context.Background()) - if err != nil { - return utils.NewHTTPError500(err, currentUserID) - } - defer tx.Rollback(context.Background()) - - _, err = db.Exec(context.Background(), fmt.Sprintf(` - SET ROLE "%[1]s"; - `, currentUserID)) - if err != nil { - return utils.NewHTTPError500(err, currentUserID) - } - - modif, err := db.Exec(context.Background(), `update bowuser SET unconfirmedemails=coalesce(unconfirmedemails, '[]'::jsonb) || $2::jsonb where id=$1;`, id, json) - - if err != nil { - return utils.NewHTTPError500(err, currentUserID) - } - - if modif.RowsAffected() != 1 { - return return utils.NewHTTPError(fmt.Sprintf("User not found '%s'", id), currentUserID, 404) - } - - err = tx.Commit(context.Background()) - if err != nil { - return utils.NewHTTPError500(err, currentUserID) - } + q := &query{sql: `update bowuser SET unconfirmedemails=coalesce(unconfirmedemails, '[]'::jsonb) || $2::jsonb where id=$1;`} + err = q.execOnOneRow(currentUserID, id, json) return token, nil } @@ -353,55 +231,27 @@ func UpdateUserAuthenticationInfo(currentUserID string, id string, auth model.Au authAsJSON, err := json.Marshal(auth) if err != nil { return err - } - - tx, err := db.Begin(context.Background()) - if err != nil { - return utils.NewHTTPError500(err, currentUserID) - } - defer tx.Rollback(context.Background()) + } - _, err = db.Exec(context.Background(), fmt.Sprintf(` - SET ROLE "%[1]s"; - `, currentUserID)) - if err != nil { - return utils.NewHTTPError500(err, currentUserID) - } - - modif, err := db.Exec(context.Background(), ` - UPDATE bowuser + q := &query{sql: `UPDATE bowuser SET (authenticationinfo) = (SELECT * FROM json_populate_record(NULL::authenticationinfo, $2::json)) - WHERE id=$1`, id, string(authAsJSON)) - - if err != nil { - return utils.NewHTTPError500(err, currentUserID) - } + WHERE id=$1`} + err = q.execOnOneRow(currentUserID, id, string(authAsJSON)) - if modif.RowsAffected() != 1 { - return return utils.NewHTTPError(fmt.Sprintf("User not found '%s'", id), currentUserID, 404) - } - - err = tx.Commit(context.Background()) - if err != nil { - return utils.NewHTTPError500(err, currentUserID) - } - - return nil + return err } /* UpdateUserActions met a jour les actions utilisateur */ -func UpdateUserActions(id string, actions []model.Action) error { +func UpdateUserActions(currentUserID string, id string, actions []model.Action) error { json, err := json.Marshal(actions) if err != nil { return err } - modif, err := db.Exec(context.Background(), `update bowuser SET actions=$2::jsonb where id=$1;`, id, json) - if modif.RowsAffected() != 1 { - return fmt.Errorf("No user found for id '%s'", id) - } + q := &query{sql: `update bowuser SET actions=$2::jsonb where id=$1;`} + err = q.execOnOneRow(currentUserID, id, json) return err } @@ -409,11 +259,9 @@ func UpdateUserActions(id string, actions []model.Action) error { /* UpdateUserAutoScreenshot met a jour le boolean d'auto screenshot de la page */ -func UpdateUserAutoScreenshot(id string, value bool) error { - modif, err := db.Exec(context.Background(), `update bowuser SET autoScreenshot=$2 where id=$1;`, id, value) - if modif.RowsAffected() != 1 { - return fmt.Errorf("No user found for id '%s'", id) - } +func UpdateUserAutoScreenshot(currentUserID string, id string, value bool) error { + q := &query{sql: `update bowuser SET autoScreenshot=$2 where id=$1;`} + err := q.execOnOneRow(currentUserID, id, value) return err } @@ -421,11 +269,9 @@ func UpdateUserAutoScreenshot(id string, value bool) error { /* UpdateUserAutoFavicon met a jour le boolean d'auto favicon de la page */ -func UpdateUserAutoFavicon(id string, value bool) error { - modif, err := db.Exec(context.Background(), `update bowuser SET autoFavicon=$2 where id=$1;`, id, value) - if modif.RowsAffected() != 1 { - return fmt.Errorf("No user found for id '%s'", id) - } +func UpdateUserAutoFavicon(currentUserID string, id string, value bool) error { + q := &query{sql: `update bowuser SET autoFavicon=$2 where id=$1;`} + err := q.execOnOneRow(currentUserID, id, value) return err } @@ -433,11 +279,9 @@ func UpdateUserAutoFavicon(id string, value bool) error { /* UpdateUserMaxTagInCloud met a jour le nombre d'element du nuage de tag */ -func UpdateUserMaxTagInCloud(id string, value int8) error { - modif, err := db.Exec(context.Background(), `update bowuser SET maxTagInCloud=$2 where id=$1;`, id, value) - if modif.RowsAffected() != 1 { - return fmt.Errorf("No user found for id '%s'", id) - } +func UpdateUserMaxTagInCloud(currentUserID string, id string, value int8) error { + q := &query{sql: `update bowuser SET maxTagInCloud=$2 where id=$1;`} + err := q.execOnOneRow(currentUserID, id, value) return err } @@ -445,11 +289,9 @@ func UpdateUserMaxTagInCloud(id string, value int8) error { /* UpdateUserMaxResult met a jour le nombre d'element affiché dans une page de resultat */ -func UpdateUserMaxResult(id string, value int8) error { - modif, err := db.Exec(context.Background(), `update bowuser SET maxResult=$2 where id=$1;`, id, value) - if modif.RowsAffected() != 1 { - return fmt.Errorf("No user found for id '%s'", id) - } +func UpdateUserMaxResult(currentUserID string, id string, value int8) error { + q := &query{sql: `update bowuser SET maxResult=$2 where id=$1;`} + err := q.execOnOneRow(currentUserID, id, value) return err } @@ -457,7 +299,7 @@ func UpdateUserMaxResult(id string, value int8) error { /* ConfirmUserEmail verif et confirme un email */ -func ConfirmUserEmail(id string, token string) error { +func ConfirmUserEmail(currentUserID string, id string, token string) error { // le but est de simplement faire passer du statut non confirmer à confirmer un email // si on le retrouve pour l'utilisateur et que le token est le bon. // mais avec le schema de base choisi c'est un poil compliqué @@ -467,7 +309,9 @@ func ConfirmUserEmail(id string, token string) error { // - on update la ligne en faisant passer l'email dans les emails valides (ajout dans emails, suppression dans unconfirmedemails) // - on garanti que personne n'a deja cette email, sinon on ne fait rien (il reste en a confirmer) jsonPathToCheckToken := fmt.Sprintf(`$[*].token ? (@ == "%s")`, token) - modif, err := db.Exec(context.Background(), ` + + + q := &query{sql: ` with ue as (select jsonb_array_elements(unconfirmedemails) as json from bowuser where b.id=$1 and unconfirmedemails @? $3), @@ -482,10 +326,8 @@ func ConfirmUserEmail(id string, token string) error { unconfirmedemails=unconfirmedemails - (data.index::int - 1) from data where b.id=$1 and b.unconfirmedemails @? $3 and - (select count(id) from bowuser where emails @> ('{' || data.email || '}')::text[]) = 0;`, id, token, jsonPathToCheckToken) + (select count(id) from bowuser where emails @> ('{' || data.email || '}')::text[]) = 0;`} + err := q.execOnOneRow(currentUserID, id, token, jsonPathToCheckToken) - if modif.RowsAffected() != 1 { - return errors.New("No user found to for this token") - } return err } diff --git a/pkg/repository/userRepository_test.go b/pkg/repository/userRepository_test.go new file mode 100644 index 0000000..3fed714 --- /dev/null +++ b/pkg/repository/userRepository_test.go @@ -0,0 +1,20 @@ +package repository + +import ( + "testing" + "regexp" +) + +func TestCreateUser(t *testing.T) { + // Init("postgres://dbuser:xxxxxxxx@localhost:5432/bow", false) + // id, err := CreateUser("poussin.test@pouss.in", "xxxxxxxx") + + // if err != nil { + // t.Error("TestCreateUser can't create user", err) + // } + + // matched, err := regexp.MatchString(`^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$`, id) + // if !matched || err != nil { + // t.Error("TestCreateUser bad id format", matched, err) + // } +} diff --git a/pkg/utils/error.go b/pkg/utils/error.go index 1c7afe3..a39c386 100644 --- a/pkg/utils/error.go +++ b/pkg/utils/error.go @@ -3,6 +3,7 @@ package utils import ( "fmt" "net/http" + "log" ) /* @@ -45,8 +46,12 @@ func NewHTTPError500(err error, currentUserID string) *httpError { func Throw(w http.ResponseWriter, err error) { if err, ok := err.(*httpError); ok { + if err.StatusCode >= 500 { + log.Println(err) + } http.Error(w, fmt.Sprintf("%s", err), err.StatusCode) } else { + log.Println(err) http.Error(w, fmt.Sprintf("%s", err), 500) } } -- To stop receiving notification emails like this one, please contact chorem.org SCM administrator <admin+scm@chorem.org>.
participants (1)
-
chorem.org scm