From 7f12e4fa01a2bf3ab25844f3d73af67881a587f1 Mon Sep 17 00:00:00 2001 From: Vidhu Kant Sharma Date: Sat, 2 Sep 2023 23:12:52 +0530 Subject: added sign-in --- auth/auth.go | 28 ++++++++++++++++++ auth/controller.go | 55 ++++++++++++++++++++++++++++++++++ auth/router.go | 29 ++++++++++++++++++ conf/conf.go | 9 ++++++ errors/errors.go | 3 ++ errors/status.go | 3 ++ main.go | 4 +++ openbills.toml | 13 ++++++++ user/controller.go | 39 ++++++++++++++++++++++++ user/hooks.go | 38 ++++++++++++++++++++++++ user/router.go | 29 ++++++++++++++++++ user/service.go | 39 ++++++++++++++++++++++++ user/user.go | 38 ++++++++++++++++++++++++ user/validators.go | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 14 files changed, 414 insertions(+) create mode 100644 auth/auth.go create mode 100644 auth/controller.go create mode 100644 auth/router.go create mode 100644 user/controller.go create mode 100644 user/hooks.go create mode 100644 user/router.go create mode 100644 user/service.go create mode 100644 user/user.go create mode 100644 user/validators.go diff --git a/auth/auth.go b/auth/auth.go new file mode 100644 index 0000000..d221224 --- /dev/null +++ b/auth/auth.go @@ -0,0 +1,28 @@ +/* openbills - Server for web based Libre Billing Software + * Copyright (C) 2023 Vidhu Kant Sharma + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package auth + +import ( + "gorm.io/gorm" + d "vidhukant.com/openbills/db" +) + +var db *gorm.DB +func init() { + db = d.DB +} diff --git a/auth/controller.go b/auth/controller.go new file mode 100644 index 0000000..bc9f15a --- /dev/null +++ b/auth/controller.go @@ -0,0 +1,55 @@ +/* openbills - Server for web based Libre Billing Software + * Copyright (C) 2023 Vidhu Kant Sharma + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package auth + +import ( + "vidhukant.com/openbills/user" + "golang.org/x/crypto/bcrypt" + "github.com/gin-gonic/gin" + "net/http" +) + +func handleSignIn (ctx *gin.Context) { + var user user.User + ctx.Bind(&user) + + var err error + + // hash password + var bytes []byte + bytes, err = bcrypt.GenerateFromPassword([]byte(user.Password), 14) + if err != nil { + // TODO: handle potential errors + ctx.Error(err) + ctx.Abort() + return + } + user.Password = string(bytes) + + err = user.Create() + if err != nil { + ctx.Error(err) + ctx.Abort() + return + } + + ctx.JSON(http.StatusOK, gin.H{ + "message": "success", + "data": user, + }) +} diff --git a/auth/router.go b/auth/router.go new file mode 100644 index 0000000..4a0bb94 --- /dev/null +++ b/auth/router.go @@ -0,0 +1,29 @@ +/* openbills - Server for web based Libre Billing Software + * Copyright (C) 2023 Vidhu Kant Sharma + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package auth + +import ( + "github.com/gin-gonic/gin" +) + +func Routes(route *gin.RouterGroup) { + g := route.Group("/auth") + { + g.POST("/sign-in", handleSignIn) + } +} diff --git a/conf/conf.go b/conf/conf.go index 58dafe1..6f9288e 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -22,6 +22,7 @@ import ( "log" ) +// TODO: validate config func init() { viper.SetConfigName("openbills") viper.SetConfigType("toml") @@ -38,7 +39,15 @@ func init() { viper.SetDefault("port", "8765") + viper.SetDefault("security.min_password_length", 12) + viper.SetDefault("security.max_password_length", 128) + viper.SetDefault("instance.title", "OpenBills") viper.SetDefault("instance.description", "Libre Billing Software") viper.SetDefault("instance.url", "https://openbills.vidhukant.com") + + // TODO: exit if these 3 have unallowed fields + viper.SetDefault("username.allowed_characters", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.-_") + viper.SetDefault("username.min_username_length", 2) + viper.SetDefault("username.max_username_length", 20) } diff --git a/errors/errors.go b/errors/errors.go index 08d8a4c..dd307e5 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -33,6 +33,8 @@ var ( ErrEmptyBrandName = errors.New("Brand Name Cannot Be Empty") ErrInvalidUnitPrice = errors.New("Invalid Unit Price") ErrInvalidGSTPercentage = errors.New("Invalid GST Percentage") + ErrPasswordTooShort = errors.New("Password Is Too Short") + ErrPasswordTooLong = errors.New("Password Is Too Long") // 404 ErrNotFound = errors.New("Not Found") @@ -42,6 +44,7 @@ var ( ErrNonUniqueGSTIN = errors.New("GSTIN Must Be Unique") ErrNonUniquePhone = errors.New("Phone Number Must Be Unique") ErrNonUniqueEmail = errors.New("Email Address Must Be Unique") + ErrNonUniqueUsername = errors.New("Email Address Must Be Unique") ErrNonUniqueWebsite = errors.New("Website Must Be Unique") ErrNonUniqueBrandName = errors.New("Brand Name Must Be Unique") ErrNonUniqueBrandItem = errors.New("Item With Same Name And Brand Already Exists") diff --git a/errors/status.go b/errors/status.go index eb59506..b0da1ae 100644 --- a/errors/status.go +++ b/errors/status.go @@ -35,6 +35,8 @@ func StatusCodeFromErr(err error) int { errors.Is(err, ErrInvalidGSTIN) || errors.Is(err, ErrEmptyBrandName) || errors.Is(err, ErrInvalidUnitPrice) || + errors.Is(err, ErrPasswordTooShort) || + errors.Is(err, ErrPasswordTooLong) || errors.Is(err, ErrInvalidGSTPercentage) { return http.StatusBadRequest } @@ -48,6 +50,7 @@ func StatusCodeFromErr(err error) int { // 409 if errors.Is(err, ErrNonUniqueGSTIN) || errors.Is(err, ErrNonUniquePhone) || + errors.Is(err, ErrNonUniqueUsername) || errors.Is(err, ErrNonUniqueEmail) || errors.Is(err, ErrNonUniqueWebsite) || errors.Is(err, ErrNonUniqueBrandName) || diff --git a/main.go b/main.go index e40ac8f..dfeec0c 100644 --- a/main.go +++ b/main.go @@ -26,6 +26,8 @@ import ( "vidhukant.com/openbills/errors" // routes + "vidhukant.com/openbills/user" + "vidhukant.com/openbills/auth" "vidhukant.com/openbills/customer" "vidhukant.com/openbills/item" @@ -47,6 +49,8 @@ func main() { api := r.Group("/api") api.Use(errors.ErrResponse()) { + user.Routes(api) + auth.Routes(api) customer.Routes(api) item.Routes(api) } diff --git a/openbills.toml b/openbills.toml index 567aa00..43b1dc4 100644 --- a/openbills.toml +++ b/openbills.toml @@ -11,3 +11,16 @@ params = "charset=utf8mb4&parseTime=True&loc=Local" title = "OpenBills" description = "Libre Billing Software" url = "https://openbills.vidhukant.com/" + +[security] +min_password_length = 12 +max_password_length = 128 + +[username] +allowed_characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.-_" +min_username_length = 2 +max_username_length = 32 + +[cryptography] +auth_secret = "22ELiOfHn19s0z1WWgsOT9RupghRYrXm" +refresh_secret = "22ELiOfHn19s0z1WWgsOT9RupghRYrXm" diff --git a/user/controller.go b/user/controller.go new file mode 100644 index 0000000..abdcc5b --- /dev/null +++ b/user/controller.go @@ -0,0 +1,39 @@ +/* openbills - Server for web based Libre Billing Software + * Copyright (C) 2023 Vidhu Kant Sharma + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package user + +import ( + "github.com/gin-gonic/gin" + "net/http" +) + +func handleGetUser (ctx *gin.Context) { + var user User + + //err = getUser(&user, uint(id)) + //if err != nil { + // ctx.Error(err) + // ctx.Abort() + // return + //} + + ctx.JSON(http.StatusOK, gin.H{ + "message": "success", + "data": user, + }) +} diff --git a/user/hooks.go b/user/hooks.go new file mode 100644 index 0000000..d49d8be --- /dev/null +++ b/user/hooks.go @@ -0,0 +1,38 @@ +/* openbills - Server for web based Libre Billing Software + * Copyright (C) 2023 Vidhu Kant Sharma + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package user + +import ( + "gorm.io/gorm" +) + +func (u *User) BeforeCreate(tx *gorm.DB) error { + var err error + + err = validatePassword(u.Password) + if err != nil { + return err + } + + err = u.validate() + if err != nil { + return err + } + + return nil +} diff --git a/user/router.go b/user/router.go new file mode 100644 index 0000000..8a9ad86 --- /dev/null +++ b/user/router.go @@ -0,0 +1,29 @@ +/* openbills - Server for web based Libre Billing Software + * Copyright (C) 2023 Vidhu Kant Sharma + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package user + +import ( + "github.com/gin-gonic/gin" +) + +func Routes(route *gin.RouterGroup) { + g := route.Group("/user") + { + g.GET("/", handleGetUser) + } +} diff --git a/user/service.go b/user/service.go new file mode 100644 index 0000000..8b3b712 --- /dev/null +++ b/user/service.go @@ -0,0 +1,39 @@ +/* openbills - Server for web based Libre Billing Software + * Copyright (C) 2023 Vidhu Kant Sharma + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package user + +func (u *User) Create() error { + res := db.Create(u) + // TODO: handle potential errors + return res.Error +} + +//func (c *Customer) del() error { +// res := db.Delete(c) +// +// // TODO: handle potential errors +// if res.Error != nil { +// return res.Error +// } +// +// if res.RowsAffected == 0 { +// return e.ErrNotFound +// } +// +// return nil +//} diff --git a/user/user.go b/user/user.go new file mode 100644 index 0000000..1324c0e --- /dev/null +++ b/user/user.go @@ -0,0 +1,38 @@ +/* openbills - Server for web based Libre Billing Software + * Copyright (C) 2023 Vidhu Kant Sharma + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package user + +import ( + "gorm.io/gorm" + d "vidhukant.com/openbills/db" +) + +var db *gorm.DB +func init() { + db = d.DB + + db.AutoMigrate(&User{}) +} + +type User struct { + gorm.Model + Username string + Email string + Password string + IsVerified bool +} diff --git a/user/validators.go b/user/validators.go new file mode 100644 index 0000000..88239c6 --- /dev/null +++ b/user/validators.go @@ -0,0 +1,87 @@ +/* openbills - Server for web based Libre Billing Software + * Copyright (C) 2023 Vidhu Kant Sharma + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package user + +import ( + "strings" + e "errors" + "github.com/spf13/viper" + "vidhukant.com/openbills/errors" +) + +func validatePassword(pass string) error { + // check if password is too short + if len(pass) < viper.GetInt("security.min_password_length") { + return errors.ErrPasswordTooShort + } + + // check if password is too long + if len(pass) > viper.GetInt("security.max_password_length") { + return errors.ErrPasswordTooLong + } + + return nil +} + +// NOTE: very inefficient and really really really dumb but it works +// TODO: find a better (or even a remotely good) way +func validateUserField(field, value string) error { + if value != "" { + var count int64 + err := db.Model(&User{}). + Where(field + " = ?", value). + Count(&count). + Error + + if err != nil { + return err + } + + if count > 0 { + switch(field) { + case "username": + return errors.ErrNonUniqueUsername + case "email": + return errors.ErrNonUniqueEmail + default: + return e.New(field + " is not unique") + } + } + } + + return nil +} + +func (u *User) validate() error { + u.Username = strings.TrimSpace(u.Username) + u.Email = strings.TrimSpace(u.Email) + u.IsVerified = false + + // TODO: validate username length and stuff + // TODO: validate if email is valid + + var err error + for _, i := range [][]string{{"username", u.Username}, {"email", u.Email}} { + err = validateUserField(i[0], i[1]) + if err != nil { + return err + } + } + + return nil +} -- cgit v1.2.3