commit 55dbc36b92791a3ca0c6ba6a46d3f701a2567b79 Author: TolaMironcenko Date: Tue May 27 18:20:05 2025 +0700 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..48e1934 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +users.db +go.sum \ No newline at end of file diff --git a/api/token.go b/api/token.go new file mode 100644 index 0000000..2672159 --- /dev/null +++ b/api/token.go @@ -0,0 +1,97 @@ +package api + +import ( + "encoding/json" + "fmt" + "form/database" + "form/model" + "maps" + "net/http" + "time" + + "github.com/golang-jwt/jwt/v5" +) + +const defaultsecretkey = "secret" + +func createJWT(secretKey string, data map[string]interface{}, ttl time.Duration) (string, error) { + claims := jwt.MapClaims{ + "exp": time.Now().Add(ttl).Unix(), + "iat": time.Now().Unix(), + "nbf": time.Now().Unix(), + } + maps.Copy(claims, data) + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + signedToken, err := token.SignedString([]byte(secretKey)) + if err != nil { + return "", nil + } + return signedToken, nil +} + +func VerifyJWT(tokenString string, secretKey string) (map[string]interface{}, error) { + token, e := jwt.Parse( + tokenString, + func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("unexpexted signing method: %v", token.Header["alg"]) + } + return []byte(secretKey), nil + }, + ) + if e != nil { + return nil, fmt.Errorf("token validation failed: %w", e) + } + if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { + result := make(map[string]interface{}) + for key, value := range claims { + if key == "exp" || key == "iat" || key == "nbf" { + continue + } + result[key] = value + } + return result, nil + } + return nil, fmt.Errorf("invalid token") +} + +func Token(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Content-Type", "application/json") + var request model.TokenRequest + decoder := json.NewDecoder(req.Body) + decoder.Decode(&request) + dbuser, e := database.GetUserByUsername(request.Username) + if e != nil { + fmt.Fprintf(w, "{\"status\":403,\"error\":\"%v\"}", e) + return + } + if dbuser.Password == request.Password { + responseUser := model.UserResponse{ + Id: dbuser.Id, + Username: dbuser.Username, + Email: dbuser.Email, + Group: dbuser.Group, + IsSuperuser: dbuser.IsSuperuser, + } + responseUserMarshal, _ := json.Marshal(responseUser) + responseUserUnmarshal := make(map[string]interface{}) + json.Unmarshal(responseUserMarshal, &responseUserUnmarshal) + token, _ := createJWT(defaultsecretkey, responseUserUnmarshal, time.Duration(24*time.Hour)) + fmt.Fprintf(w, "{\"token\":\"%s\"}", token) + return + } + fmt.Fprintf(w, "{\"status\":\"403\"}") +} + +func VerifyToken(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Content-Type", "application/json") + var request model.Token + decoder := json.NewDecoder(req.Body) + decoder.Decode(&request) + _, e := VerifyJWT(request.Token, defaultsecretkey) + if e != nil { + fmt.Fprintf(w, "{\"status\":403}") + return + } + fmt.Fprintf(w, "{\"status\":200}") +} diff --git a/api/users.go b/api/users.go new file mode 100644 index 0000000..1f91ea7 --- /dev/null +++ b/api/users.go @@ -0,0 +1,89 @@ +package api + +import ( + "encoding/json" + "fmt" + "form/database" + "form/model" + "net/http" +) + +func GetAllUsers(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Content-Type", "application/json") + var request model.GetAllUsersRequest + decoder := json.NewDecoder(req.Body) + decoder.Decode(&request) + claims, e := VerifyJWT(request.Token, defaultsecretkey) + if e != nil || claims["is_superuser"] != true { + fmt.Fprintf(w, "{\"status\":403}") + return + } + users, e := database.GetAllUsers() + if e != nil { + fmt.Fprintf(w, "{\"error\":\"%v\"}", e) + return + } + var responseUsers []model.UserResponse + for i := range users { + responseUsers = append(responseUsers, model.UserResponse{ + Id: users[i].Id, + Username: users[i].Username, + Email: users[i].Email, + Group: users[i].Group, + IsSuperuser: users[i].IsSuperuser, + }) + } + jsondata, _ := json.Marshal(responseUsers) + fmt.Fprint(w, string(jsondata)) +} + +func GetUser(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Content-Type", "application/json") + var request model.Token + decoder := json.NewDecoder(req.Body) + decoder.Decode(&request) + claims, e := VerifyJWT(request.Token, defaultsecretkey) + if e != nil { + fmt.Fprintf(w, "{\"status\":403}") + return + } + user, e := database.GetUserByUsername(fmt.Sprintf("%v", claims["username"])) + if e != nil { + fmt.Fprintf(w, "{\"error\":\"%v\"}", e) + return + } + responseUser := model.UserResponse{ + Id: user.Id, + Username: user.Username, + Email: user.Email, + Group: user.Group, + IsSuperuser: user.IsSuperuser, + } + jsondata, _ := json.Marshal(responseUser) + fmt.Fprint(w, string(jsondata)) +} + +func ChangePassword(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Content-Type", "application/json") + +} + +func DelUser(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Content-Type", "application/json") + +} + +func AddUser(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Content-Type", "application/json") + +} + +func RegisterUser(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Content-Type", "application/json") + +} + +func ChangeUser(w http.ResponseWriter, req *http.Request) { + w.Header().Set("Content-Type", "application/json") + +} diff --git a/database/db.go b/database/db.go new file mode 100644 index 0000000..217cd58 --- /dev/null +++ b/database/db.go @@ -0,0 +1,25 @@ +package database + +import ( + "fmt" + "form/model" + + "gorm.io/driver/sqlite" + "gorm.io/gorm" +) + +var db *gorm.DB + +func Init() error { + var err error + db, err = gorm.Open(sqlite.Open("users.db"), &gorm.Config{}) + if err != nil { + fmt.Println("failed to connect database") + return err + } + return nil +} + +func Migrate() { + db.AutoMigrate(&model.User{}) +} diff --git a/database/users.go b/database/users.go new file mode 100644 index 0000000..20e839b --- /dev/null +++ b/database/users.go @@ -0,0 +1,85 @@ +package database + +import ( + "encoding/json" + "errors" + "fmt" + "form/model" + "os" + "strings" + + "gorm.io/gorm" +) + +func CreateUser(user *model.User) error { + e := db.Where(model.User{ + Username: user.Username, + }).FirstOrCreate(&user).Error + if e != nil { + return e + } + return nil +} + +func GetUserById(id string) (*model.User, error) { + var user model.User + e := db.First(&user, "id = ?", id).Error + if e != nil { + if errors.Is(e, gorm.ErrRecordNotFound) { + return nil, fmt.Errorf("user not found") + } + return nil, e + } + return &user, nil +} + +func GetUserByUsername(username string) (*model.User, error) { + var user model.User + e := db.First(&user, "username = ?", username).Error + if e != nil { + if errors.Is(e, gorm.ErrRecordNotFound) { + return nil, fmt.Errorf("user not found") + } + return nil, e + } + return &user, nil +} + +func DeleteUser(id string) error { + var user model.User + e := db.First(&user, "id = ?", id).Error + if e != nil { + if errors.Is(e, gorm.ErrRecordNotFound) { + return fmt.Errorf("user not found") + } + return e + } + e = db.Delete(&user).Error + if e != nil { + if errors.Is(e, gorm.ErrRecordNotFound) { + return fmt.Errorf("user not found") + } + return e + } + return nil +} + +func GetUsersFromFile(filename string) []model.User { + data, e := os.ReadFile(filename) + if e != nil { + panic(e) + } + decoder := json.NewDecoder(strings.NewReader(string(data))) + var res []model.User + decoder.Decode(&res) + return res +} + +func GetAllUsers() ([]model.User, error) { + var users []model.User + e := db.Find(&users).Error + if e != nil { + return nil, e + } + return users, nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..0c62eab --- /dev/null +++ b/go.mod @@ -0,0 +1,16 @@ +module form + +go 1.24.3 + +require ( + github.com/golang-jwt/jwt/v5 v5.2.2 + gorm.io/driver/sqlite v1.5.7 + gorm.io/gorm v1.30.0 +) + +require ( + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/mattn/go-sqlite3 v1.14.28 // indirect + golang.org/x/text v0.25.0 // indirect +) diff --git a/main.go b/main.go new file mode 100644 index 0000000..f715110 --- /dev/null +++ b/main.go @@ -0,0 +1,30 @@ +package main + +import ( + "form/api" + "form/database" + "net/http" +) + +func main() { + database.Init() + database.Migrate() + + users := database.GetUsersFromFile("users.json") + for i := range users { + database.CreateUser(&users[i]) + } + + http.HandleFunc("/api/token", api.Token) + http.HandleFunc("/api/token/verify", api.VerifyToken) + http.HandleFunc("/api/access", api.VerifyToken) + http.HandleFunc("/api/register", api.RegisterUser) + + http.HandleFunc("/api/users", api.GetAllUsers) + http.HandleFunc("/api/users/user", api.GetUser) + http.HandleFunc("/api/users/password", api.ChangePassword) + http.HandleFunc("/api/users/change", api.ChangeUser) + http.HandleFunc("/api/users/del", api.DelUser) + http.HandleFunc("/api/user/add", api.AddUser) + http.ListenAndServe(":8090", nil) +} diff --git a/model/user.go b/model/user.go new file mode 100644 index 0000000..ddba06c --- /dev/null +++ b/model/user.go @@ -0,0 +1,34 @@ +package model + +import "gorm.io/gorm" + +type User struct { + gorm.Model + Id string `json:"id"` + Username string `json:"username"` + Email string `json:"email"` + Password string `json:"password"` + Group string `json:"group"` + IsSuperuser bool `json:"is_superuser"` +} + +type UserResponse struct { + Id string `json:"id"` + Username string `json:"username"` + Email string `json:"email"` + Group string `json:"group"` + IsSuperuser bool `json:"is_superuser"` +} + +type TokenRequest struct { + Username string `json:"username"` + Password string `json:"password"` +} + +type Token struct { + Token string `json:"token"` +} + +type GetAllUsersRequest struct { + Token string `json:"token"` +} diff --git a/users.json b/users.json new file mode 100644 index 0000000..aefaab8 --- /dev/null +++ b/users.json @@ -0,0 +1,18 @@ +[ + { + "id": "0", + "username": "tola", + "email": "mail@example.com", + "password": "2808", + "group": "root", + "is_superuser": true + }, + { + "id": "1", + "username": "lisa", + "email": "lisa@example.com", + "password": "asdf", + "group": "users", + "is_superuser": false + } +]