diff --git a/authorized.go b/authorized.go new file mode 100644 index 0000000..d4f85bb --- /dev/null +++ b/authorized.go @@ -0,0 +1,14 @@ +package esproject + +type AuthorizedUser struct { + Id int64 `json:"id" gorm:"unique;primaryKey;autoIncrement"` + Username string `json:"username"` + FirstName string `json:"first_name"` + LastName string `json:"last_name"` + PictureURL string `json:"picture_url"` + Email string `json:"email"` + Access string `json:"access"` + + TelegramId string `json:"telegram_id"` + GoogleId string `json:"google_id"` +} diff --git a/pkg/handler/middleware.go b/pkg/handler/middleware.go new file mode 100644 index 0000000..eca3a33 --- /dev/null +++ b/pkg/handler/middleware.go @@ -0,0 +1,30 @@ +package handler + +import ( + "esproject/pkg/util" + "net/http" + + "github.com/gin-gonic/gin" +) + +const ( + authorizationHeader = "Authorization" + userCtx = "UserInfo" + userHeader = "UserHeader" +) + +func (h *Handler) userIdentity(c *gin.Context) { + header := c.GetHeader(authorizationHeader) + if header == "" { + util.NewErrorResponse(c, http.StatusUnauthorized, "empty auth header") + return + } + + user, err := h.services.Authorization.Authorize(header) + if err != nil { + util.NewErrorResponse(c, http.StatusUnauthorized, err.Error()) + } + + c.Set(userCtx, user.Id) + c.Set(userHeader, header) +} diff --git a/pkg/repository/database.go b/pkg/repository/database.go new file mode 100644 index 0000000..9eca5c9 --- /dev/null +++ b/pkg/repository/database.go @@ -0,0 +1,46 @@ +package repository + +import ( + "esproject" + "fmt" + + "gorm.io/driver/mysql" + "gorm.io/gorm" + "gorm.io/gorm/logger" +) + +type Config struct { + Host string + Port string + Username string + Password string + DBName string +} + +func NewDB(cfg Config) (*gorm.DB, error) { + dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", + cfg.Username, + cfg.Password, + cfg.Host, + cfg.Port, + cfg.DBName) + + db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{ + Logger: logger.Default.LogMode(logger.Silent), + }) + if err != nil { + return nil, err + } + + err = db.Set("gorm:table_options", "CHARSET=utf8mb4").AutoMigrate(&esproject.Project{}) + if err != nil { + return nil, err + } + + err = db.Set("gorm:table_options", "CHARSET=utf8mb4").AutoMigrate(&esproject.ProjectInvite{}) + if err != nil { + return nil, err + } + + return db, nil +} diff --git a/pkg/service/service.go b/pkg/service/service.go new file mode 100644 index 0000000..64ca3a5 --- /dev/null +++ b/pkg/service/service.go @@ -0,0 +1,21 @@ +package service + +import ( + "esproject" +) + +type Authorization interface { + Authorize(header string) (*esproject.AuthorizedUser, error) + AuthorizeById(id int64) (*esproject.AuthorizedUser, error) + AuthorizeAndUpdatePicture(header string, picUrl string) (*esproject.AuthorizedUser, error) +} + +type Service struct { + Authorization +} + +func NewService() *Service { + return &Service{ + Authorization: NewAuthService(), + } +} diff --git a/pkg/util/response.go b/pkg/util/response.go new file mode 100644 index 0000000..5d15a96 --- /dev/null +++ b/pkg/util/response.go @@ -0,0 +1,15 @@ +package util + +import ( + "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" +) + +type error struct { + Message string `json:"message"` +} + +func NewErrorResponse(c *gin.Context, statusCode int, message string) { + logrus.Error(message) + c.AbortWithStatusJSON(statusCode, error{Message: message}) +} diff --git a/pkg/util/role.go b/pkg/util/role.go new file mode 100644 index 0000000..a5c93f8 --- /dev/null +++ b/pkg/util/role.go @@ -0,0 +1,50 @@ +package util + +import ( + "encoding/json" + "esproject" + "fmt" + "github.com/gin-gonic/gin" + "io" + "net/http" +) + +func MatchRole(c *gin.Context, role string) bool { + client := &http.Client{} + req, err := http.NewRequest("GET", fmt.Sprintf("https://auth.easystartup.su/api/user/self"), nil) + if err != nil { + return false + } + + req.Header.Add("Authorization", c.GetHeader("Authorization")) + req.Header.Add("Accept", "application/json") + + resp, err := client.Do(req) + if err != nil { + return false + } + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + + } + }(resp.Body) + + // Check if the response status is 200 OK + if resp.StatusCode != http.StatusOK { + return false + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + return false + } + + var result esproject.AuthorizedUser + err = json.Unmarshal(body, &result) + if err != nil { + return false + } + + return result.Access == role +} diff --git a/project.go b/project.go new file mode 100644 index 0000000..1d5cced --- /dev/null +++ b/project.go @@ -0,0 +1,50 @@ +package esproject + +import ( + "database/sql/driver" + "encoding/json" + "fmt" +) + +type StringSlice []string +type Int64Slice []int64 + +type Project struct { + Id int64 `json:"id" gorm:"unique;primaryKey;autoIncrement"` + Name string `json:"name"` + OwnerId int64 `json:"owner_id"` + Tags StringSlice `json:"tags" gorm:"type:text"` + Members Int64Slice `json:"members" gorm:"type:text"` +} + +type ProjectInvite struct { + Id int64 `json:"id" gorm:"unique;primaryKey;autoIncrement"` + Project int64 `json:"project"` + User int64 `json:"user"` +} + +// Value Implementing the driver.Valuer interface for StringSlice +func (s StringSlice) Value() (driver.Value, error) { + return json.Marshal(s) +} + +// Scan Implementing the sql.Scanner interface for StringSlice +func (s *StringSlice) Scan(value interface{}) error { + if err := json.Unmarshal(value.([]byte), s); err != nil { + return fmt.Errorf("failed to unmarshal StringSlice: %w", err) + } + return nil +} + +// Value Implementing the driver.Valuer interface for Int64Slice +func (s Int64Slice) Value() (driver.Value, error) { + return json.Marshal(s) +} + +// Scan Implementing the sql.Scanner interface for Int64Slice +func (s *Int64Slice) Scan(value interface{}) error { + if err := json.Unmarshal(value.([]byte), s); err != nil { + return fmt.Errorf("failed to unmarshal Int64Slice: %w", err) + } + return nil +}