Browse Source

chore: use simpler auth role controls

main
Evan 7 months ago
parent
commit
4f3a8b62b3
  1. 16
      internal/constants/constant.go
  2. 13
      models/auth.go
  3. 3
      models/calendar.go
  4. 31
      models/event.go
  5. 13
      models/models.go
  6. 6
      models/user.go
  7. 27
      modules/authenticate/middleware.go
  8. 127
      services/auth/auth.go

16
internal/constants/constant.go

@ -0,0 +1,16 @@
package constants
//Role enum of user permission roles
type Role int
//Role enum of user permission roles
const (
Root Role = iota
Admin
User
Guest
)
func (r Role) String() string {
return [...]string{"Root", "Admin", "User", "Guest"}[r]
}

13
models/auth.go

@ -1,13 +0,0 @@
package models
import (
"gorm.io/gorm"
)
// Role User groups
type Role struct {
gorm.Model
Name string `gorm:"unique;not null"`
Level int8 `gorm:"default:2"`
Users []*User `gorm:"many2many:user_roles;"`
}

3
models/calendar.go

@ -10,10 +10,11 @@ import (
//Calendar Calendar Model
type Calendar struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;"`
Summary string ``
Summary string
TimeZone *string
CreatedAt time.Time
UpdatedAt time.Time
Events []Event `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL"`
}
func (calendar *Calendar) BeforeCreate(tx *gorm.DB) (err error) {

31
models/event.go

@ -1 +1,32 @@
package models
import (
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
//Event Model
type Event struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;"`
CalendarID uuid.UUID `gorm:"type:uuid"`
Summary string
TimeZone *string
CreatedAt time.Time
UpdatedAt time.Time
}
func (event *Event) BeforeCreate(tx *gorm.DB) (err error) {
event.ID = uuid.New()
return
}
//InsertEvent inserts a new event to calendar.
func InsertEvent() {
db.Create(
&Event{
Summary: "title",
TimeZone: &TimeZone,
})
}

13
models/models.go

@ -5,6 +5,7 @@ import (
"path/filepath"
"github.com/google/uuid"
"github.com/mutsuki333/calendar/internal/constants"
"github.com/spf13/viper"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
@ -40,8 +41,8 @@ func InitSqlite() {
}
func migrate() {
db.AutoMigrate(&User{}, &Role{})
db.AutoMigrate(&Calendar{})
db.AutoMigrate(&User{})
db.AutoMigrate(&Calendar{}, &Event{})
setDefaultValues()
}
@ -52,9 +53,7 @@ func setDefaultValues() {
root = User{
Name: "root",
Email: "root@" + viper.GetString("app.domain"),
Roles: []*Role{
{Name: "root", Level: 0},
},
Level: constants.Root,
}
db.Create(&root)
root.ChangePassword("root")
@ -64,9 +63,7 @@ func setDefaultValues() {
admin = User{
Name: "admin",
Email: "admin@" + viper.GetString("app.domain"),
Roles: []*Role{
{Name: "admin", Level: 1},
},
Level: constants.Admin,
}
db.Create(&admin)
admin.ChangePassword("admin")

6
models/user.go

@ -4,6 +4,7 @@ import (
"errors"
"strings"
"github.com/mutsuki333/calendar/internal/constants"
"github.com/mutsuki333/calendar/internal/logger"
"golang.org/x/crypto/bcrypt"
@ -16,7 +17,7 @@ type User struct {
Name string `gorm:"unique;not null"`
Email string `gorm:"unique;not null"`
Password string
Roles []*Role `gorm:"many2many:user_roles;"`
Level constants.Role
}
//ChangePassword Change user password to *to*, return nil if success.
@ -48,6 +49,7 @@ func InsertUser(name, email, password string) error {
Name: strings.TrimSpace(name),
Email: email,
Password: string(bytes),
Level: constants.User,
}
if err := db.Create(&user).Error; err != nil {
logger.Err.Println(err)
@ -70,6 +72,6 @@ func LoadUser(userID uint) (user User, err error) {
//ListUsers lists all users
func ListUsers() (users []User) {
db.Select("id", "name", "email", "created_at").Find(&users)
db.Select("id", "name", "email", "created_at", "level").Find(&users)
return
}

27
modules/authenticate/middleware.go

@ -6,6 +6,8 @@ import (
"time"
"github.com/gin-gonic/gin"
"github.com/mutsuki333/calendar/internal/constants"
"github.com/mutsuki333/calendar/models"
"github.com/spf13/viper"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
@ -74,8 +76,29 @@ func LogoutUser(c *gin.Context) {
db.Delete(&session)
}
func verifyUser() {
// LoadUser loads user and aborts if permition denied
func LoadUser(c *gin.Context, level ...constants.Role) (user models.User, ok bool) {
s, ok := c.Get("GCSID")
if !ok {
c.AbortWithStatus(401)
return
}
session, ok := s.(Session)
if !ok {
c.AbortWithStatus(401)
return
}
user, err := models.LoadUser(session.UserID)
if err != nil {
ok = false
return
}
if len(level) > 0 && user.Level > level[0] {
c.AbortWithStatus(403)
ok = false
return
}
return
}
func newSessionID(userID uint) string {

127
services/auth/auth.go

@ -16,17 +16,6 @@ func RegisterAuthService(api *gin.RouterGroup) {
auth.GET("/", getAuthStatus)
auth.POST("/login", login)
auth.POST("/logout", logout)
auth.GET("/role/", listRoles)
auth.POST("/role/", insertRole)
auth.PUT("/role/", updateRole)
auth.DELETE("/role/", deleteRole)
auth.POST("/addrole", add2role)
auth.POST("/removerole", remove2role)
}
type userRoleRequest struct {
UserID int `json:"userID" example:"1" binding:"required"`
RoleID int `json:"roleID" example:"1" binding:"required"`
}
// @Summary Get Auth Status
@ -37,7 +26,7 @@ type userRoleRequest struct {
// @Failure 400
// @Router /auth [get]
func getAuthStatus(c *gin.Context) {
user, ok := loadUser(c)
user, ok := authenticate.LoadUser(c)
if !ok {
return
}
@ -47,91 +36,6 @@ func getAuthStatus(c *gin.Context) {
}
// @Summary List Role
// @Description List all roles
// @tags Auth
// @Produce json
// @Success 200
// @Failure 400
// @Router /auth/role [get]
func listRoles(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "listRoles",
})
}
// @Summary Insert Role
// @Description Insert a role
// @tags Auth
// @Accept json
// @Produce json
// @Success 200
// @Failure 400
// @Router /auth/role [post]
func insertRole(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "insertRole",
})
}
// @Summary Update Role
// @Description Update a role
// @tags Auth
// @Accept json
// @Produce json
// @Success 200
// @Failure 400
// @Router /auth/role [put]
func updateRole(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "updateRole",
})
}
// @Summary Delete Role
// @Description Delete a role
// @tags Auth
// @Accept json
// @Produce json
// @Success 200
// @Failure 400
// @Router /auth/role [delete]
func deleteRole(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "deleteRole",
})
}
// @Summary Add Role To User
// @Description Adds a role to user
// @tags Auth
// @Accept json
// @Produce json
// @Param payload body userRoleRequest true "payload"
// @Success 200
// @Failure 400
// @Router /auth/addrole [post]
func add2role(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "add2role",
})
}
// @Summary Remove Role From User
// @Description Remove role from user
// @tags Auth
// @Accept json
// @Produce json
// @Param payload body userRoleRequest true "payload"
// @Success 200
// @Failure 400
// @Router /auth/removerole [post]
func remove2role(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "remove2role",
})
}
type loginRequest struct {
Username string `json:"username" example:"admin" binding:"required"`
PwdB64 string `json:"password" example:"YWRtaW4=" binding:"required"`
@ -139,7 +43,7 @@ type loginRequest struct {
// @Summary Login
// @Description Login
// @tags User
// @tags Auth
// @Accept json
// @Produce json
// @Param body body loginRequest true "Request Body"
@ -173,7 +77,7 @@ func login(c *gin.Context) {
// @Summary Logout
// @Description Logout
// @tags User
// @tags Auth
// @Produce json
// @Success 200
// @Failure 400
@ -183,20 +87,19 @@ func logout(c *gin.Context) {
resource.Ok(c)
}
func loadUser(c *gin.Context) (user models.User, ok bool) {
s, ok := c.Get("GCSID")
if !ok {
c.AbortWithStatus(401)
return
}
session, ok := s.(authenticate.Session)
// @Summary Get Token
// @Description Get Api Token
// @tags Auth
// @Produce json
// @Success 200
// @Failure 400
// @Router /auth/token [post]
func token(c *gin.Context) {
user, ok := authenticate.LoadUser(c)
if !ok {
c.AbortWithStatus(401)
return
}
user, err := models.LoadUser(session.UserID)
if err != nil {
ok = false
}
return
c.JSON(http.StatusOK, gin.H{
"message": user.Name,
})
}