From 987fd909a9878315c5a2ee4cf6bb5555f9c111ed Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Sun, 12 Feb 2023 23:21:07 +0000 Subject: [PATCH] user management pt1 --- gupm.json | 6 ++-- src/user/create.go | 64 ++++++++++++++++++++++++++++++++++++++ src/user/login.go | 55 ++++++++++++++++++++++++++++++-- src/user/register.go | 74 +++++++++++++++++++++++++++++++------------- src/user/resend.go | 59 +++++++++++++++++++++++++++++++++++ src/utils/config.go | 8 ++--- src/utils/types.go | 2 ++ src/utils/utils.go | 16 ++++++++++ 8 files changed, 254 insertions(+), 30 deletions(-) create mode 100644 src/user/create.go create mode 100644 src/user/resend.go diff --git a/gupm.json b/gupm.json index 5ffe4cd..9273575 100644 --- a/gupm.json +++ b/gupm.json @@ -2,13 +2,15 @@ "author": "Yann Stepienik", "cli": { "aliases": { - "start": "build/bin", - "certificate": "sh generate-certificate.sh" + "certificate": "sh generate-certificate.sh", + "start": "build/bin" } }, "dependencies": { "default": { "go://github.com/go-playground/validator/v10": "master", + "go://github.com/golang-jwt/jwt": "master", + "go://github.com/golang/crypto/": "master", "go://github.com/gorilla/mux": "master", "go://github.com/imdario/mergo": "master", "go://github.com/joho/godotenv": "master", diff --git a/src/user/create.go b/src/user/create.go new file mode 100644 index 0000000..a1eb50a --- /dev/null +++ b/src/user/create.go @@ -0,0 +1,64 @@ +package user + +import ( + "net/http" + "log" + // "io" + // "os" + "encoding/json" + "go.mongodb.org/mongo-driver/mongo" + "time" + // "golang.org/x/crypto/bcrypt" + + "../utils" +) + +func UserCreate(w http.ResponseWriter, req *http.Request) { + utils.SetHeaders(w) + + if(req.Method == "POST") { + nickname := req.FormValue("nickname") + + c := utils.GetCollection(utils.GetRootAppId(), "users") + + user := utils.User{} + + err := c.FindOne(nil, map[string]interface{}{ + "Nickname": nickname, + }).Decode(&user) + + if err != mongo.ErrNoDocuments { + log.Println("UserCreation: User already exists") + http.Error(w, "User Creation Error", http.StatusNotFound) + } else if err != nil { + log.Println("UserCreation: Error while finding user") + http.Error(w, "User Creation Error", http.StatusInternalServerError) + } else { + + RegisterKey := utils.GenerateRandomString(24) + RegisterKeyExp := time.Now().Add(time.Hour * 24 * 7) + + _, err := c.InsertOne(nil, map[string]interface{}{ + "Nickname": nickname, + "Password": "", + "RegisterKey": RegisterKey, + "RegisterKeyExp": RegisterKeyExp, + "Role": utils.USER, + }) + + if err != nil { + log.Println("UserCreation: Error while creating user") + http.Error(w, "User Creation Error", http.StatusInternalServerError) + } + + json.NewEncoder(w).Encode(map[string]interface{}{ + "Status": "OK", + "RegisterKey": RegisterKey, + "RegisterKeyExp": RegisterKeyExp, + }) + } + } else { + log.Println("UserCreation: Method not allowed" + req.Method) + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + } +} \ No newline at end of file diff --git a/src/user/login.go b/src/user/login.go index 37f6e55..9b1378c 100644 --- a/src/user/login.go +++ b/src/user/login.go @@ -3,7 +3,12 @@ package user import ( "net/http" "log" + "math/rand" "encoding/json" + "go.mongodb.org/mongo-driver/mongo" + "time" + "golang.org/x/crypto/bcrypt" + "github.com/golang-jwt/jwt" "../utils" ) @@ -12,11 +17,55 @@ func UserLogin(w http.ResponseWriter, req *http.Request) { utils.SetHeaders(w) if(req.Method == "POST") { + time.Sleep(time.Duration(rand.Float64()*2)*time.Second) + nickname := req.FormValue("nickname") password := req.FormValue("password") - log.Println("UserLogin: nickname: " + nickname) - log.Println("UserLogin: password: " + password) // im just testing ok dont panic + err := bcrypt.CompareHashAndPassword([]byte(utils.GetHash()), []byte(password)) + + if err != nil { + log.Println("UserLogin: Encryption error") + http.Error(w, "User Logging Error", http.StatusUnauthorized) + } + + c := utils.GetCollection(utils.GetRootAppId(), "users") + + err = c.FindOne(nil, map[string]interface{}{ + "Nickname": nickname, + "Password": password, + }).Decode(&utils.User{}) + + if err == mongo.ErrNoDocuments { + log.Println("UserLogin: User not found") + http.Error(w, "User Logging Error", http.StatusNotFound) + } else if err != nil { + log.Println("UserLogin: Error while finding user") + http.Error(w, "User Logging Error", http.StatusInternalServerError) + } else { + token := jwt.New(jwt.SigningMethodEdDSA) + claims := token.Claims.(jwt.MapClaims) + claims["exp"] = time.Now().Add(30 * 24 * time.Hour) + claims["authorized"] = true + claims["nickname"] = nickname + + tokenString, err := token.SignedString(utils.GetPrivateAuthKey()) + + if err != nil { + log.Println("UserLogin: Error while signing token") + http.Error(w, "User Logging Error", http.StatusInternalServerError) + } + + expiration := time.Now().Add(30 * 24 * time.Hour) + + cookie := http.Cookie{ + Name: "jwttoken", + Value: tokenString, + Expires: expiration, + } + + http.SetCookie(w, &cookie) + } json.NewEncoder(w).Encode(map[string]interface{}{ "Status": "OK", @@ -25,4 +74,4 @@ func UserLogin(w http.ResponseWriter, req *http.Request) { log.Println("UserLogin: Method not allowed" + req.Method) http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) } -} \ No newline at end of file +} diff --git a/src/user/register.go b/src/user/register.go index 4a4ef63..b399a8d 100644 --- a/src/user/register.go +++ b/src/user/register.go @@ -3,9 +3,11 @@ package user import ( "net/http" "log" - // "io" - // "os" + "math/rand" "encoding/json" + "go.mongodb.org/mongo-driver/mongo" + "time" + "golang.org/x/crypto/bcrypt" "../utils" ) @@ -13,31 +15,61 @@ import ( func UserRegister(w http.ResponseWriter, req *http.Request) { utils.SetHeaders(w) - if(req.Method == "GET") { - // sedn form - } if(req.Method == "POST") { - // check origin + time.Sleep(time.Duration(rand.Float64()*2)*time.Second) - // check password strength + nickname := req.FormValue("nickname") + registerKey := req.FormValue("registerKey") + password := req.FormValue("password") - user := &utils.User{ - Nickname: req.FormValue("nickname"), - Password: req.FormValue("password"), - Role: (utils.Role)(req.FormValue("role")), + err := bcrypt.CompareHashAndPassword([]byte(utils.GetHash()), []byte(password)) + + if err != nil { + log.Println("UserRegister: Encryption error") + http.Error(w, "User Register Error", http.StatusUnauthorized) } - err := utils.Validate.Struct(user) + c := utils.GetCollection(utils.GetRootAppId(), "users") - if(err != nil) { - log.Fatal(err) + user := utils.User{} + + err = c.FindOne(nil, map[string]interface{}{ + "Nickname": nickname, + "RegisterKey": registerKey, + "Password": "", + }).Decode(&user) + + if err == mongo.ErrNoDocuments { + log.Println("UserRegister: User not found") + http.Error(w, "User Register Error", http.StatusNotFound) + } else if !user.RegisterKeyExp.Before(time.Now()) { + log.Println("UserRegister: Link expired") + http.Error(w, "User Register Error", http.StatusNotFound) + } else if err != nil { + log.Println("UserRegister: Error while finding user") + http.Error(w, "User Register Error", http.StatusInternalServerError) + } else { + _, err := c.UpdateOne(nil, map[string]interface{}{ + "Nickname": nickname, + "RegisterKey": registerKey, + "Password": "", + }, map[string]interface{}{ + "Password": password, + "RegisterKey": "", + "RegisterKeyExp": time.Time{}, + }) + + if err != nil { + log.Println("UserRegister: Error while updating user") + http.Error(w, "User Register Error", http.StatusInternalServerError) + } } - - req.ParseForm() + + json.NewEncoder(w).Encode(map[string]interface{}{ + "Status": "OK", + }) + } else { + log.Println("UserRegister: Method not allowed" + req.Method) + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) } - - // return json object - json.NewEncoder(w).Encode(map[string]interface{}{ - "Status": "OK", - }) } \ No newline at end of file diff --git a/src/user/resend.go b/src/user/resend.go new file mode 100644 index 0000000..17b3ef3 --- /dev/null +++ b/src/user/resend.go @@ -0,0 +1,59 @@ +package user + +import ( + "net/http" + "log" + "encoding/json" + "go.mongodb.org/mongo-driver/mongo" + "time" + + "../utils" +) + +func UserResendInviteLink(w http.ResponseWriter, req *http.Request) { + utils.SetHeaders(w) + + if(req.Method == "POST") { + id := req.FormValue("id") + + c := utils.GetCollection(utils.GetRootAppId(), "users") + + user := utils.User{} + + err := c.FindOne(nil, map[string]interface{}{ + "_id": id, + }).Decode(&user) + + if err == mongo.ErrNoDocuments { + log.Println("UserResend: User not found") + http.Error(w, "User Resend Invite Error", http.StatusNotFound) + } else if err != nil { + log.Println("UserResend: Error while finding user") + http.Error(w, "User Resend Invite Error", http.StatusInternalServerError) + } else { + RegisterKeyExp := time.Now().Add(time.Hour * 24 * 7) + + _, err := c.UpdateOne(nil, map[string]interface{}{ + "_id": id, + }, map[string]interface{}{ + "$set": map[string]interface{}{ + "RegisterKeyExp": RegisterKeyExp, + }, + }) + + if err != nil { + log.Println("UserResend: Error while updating user") + http.Error(w, "User Resend Invite Error", http.StatusInternalServerError) + } + + json.NewEncoder(w).Encode(map[string]interface{}{ + "Status": "OK", + "RegisterKey": user.RegisterKey, + "RegisterKeyExp": RegisterKeyExp, + }) + } + } else { + log.Println("UserResend: Method not allowed" + req.Method) + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + } +} \ No newline at end of file diff --git a/src/utils/config.go b/src/utils/config.go index 4ce2738..56accff 100644 --- a/src/utils/config.go +++ b/src/utils/config.go @@ -21,9 +21,9 @@ var defaultConfig = GucoConfiguration{ } func GetConfigs() GucoConfiguration { - c := GetCollection("GUCO", "Configurations") + c := GetCollection(GetRootAppId(), "Configurations") config := GucoConfiguration{} - err := c.FindOne(context.TODO(), bson.M{"_id": "GUCO"}).Decode(&config) + err := c.FindOne(context.TODO(), bson.M{"_id": GetRootAppId()}).Decode(&config) if err == mongo.ErrNoDocuments { log.Println("Record does not exist") } else if err != nil { @@ -40,10 +40,10 @@ func SetConfig(config GucoConfiguration) { mergo.Merge(&config, currentConfig) - c := GetCollection("GUCO", "Configurations") + c := GetCollection(GetRootAppId(), "Configurations") opts := options.Update().SetUpsert(true) - filter := bson.D{{"_id", "GUCO"}} + filter := bson.D{{"_id", GetRootAppId()}} update := bson.D{{"$set", config}} _, err := c.UpdateOne(context.Background(), filter, update, opts) diff --git a/src/utils/types.go b/src/utils/types.go index 6f41b19..7605c7b 100644 --- a/src/utils/types.go +++ b/src/utils/types.go @@ -25,5 +25,7 @@ type FileStats struct { type User struct { Nickname string `validate:"required"` Password string `validate:"required"` + RegisterKey string + RegisterKeyExp time.Time Role Role `validate:"required"` } \ No newline at end of file diff --git a/src/utils/utils.go b/src/utils/utils.go index e82dbf5..eaddc3c 100644 --- a/src/utils/utils.go +++ b/src/utils/utils.go @@ -35,4 +35,20 @@ func FileExists(path string) bool { } log.Println(err) return false +} + +func GetHash() string { + return "hash" +} + +func GetRootAppId() string { + return "GUCO" +} + +func GetPrivateAuthKey() string { + return "private" +} + +func GenerateRandomString(len int) string { + return "random" } \ No newline at end of file