forked from goffee/cup
Compare commits
8 commits
Author | SHA1 | Date | |
---|---|---|---|
|
49ceb4236d | ||
|
d840d3eba4 | ||
3c0a2ba991 | |||
7c033cba1d | |||
d431963181 | |||
43f3ad986e | |||
cac2986b59 | |||
|
83f0db05da |
13 changed files with 925 additions and 4 deletions
|
@ -19,6 +19,29 @@ import (
|
|||
|
||||
func AdminUsersList(c *core.Context) *core.Response {
|
||||
|
||||
// initiate authority
|
||||
auth := new(utils.Authority)
|
||||
var session = new(utils.SessionUser)
|
||||
// true if session is active
|
||||
hassession := session.Init(c)
|
||||
|
||||
if !hassession {
|
||||
type emptytemplate struct{}
|
||||
emptyData := emptytemplate{}
|
||||
return c.Response.Template("nopermission.html", emptyData)
|
||||
}
|
||||
|
||||
session_uid := session.GetUserID()
|
||||
// check if user has role admin
|
||||
is_admin, _ := auth.CheckUserRole(c, session_uid, "admin")
|
||||
|
||||
if !is_admin {
|
||||
type emptytemplate struct{}
|
||||
emptyData := emptytemplate{}
|
||||
return c.Response.Template("nopermission.html", emptyData)
|
||||
}
|
||||
|
||||
// continue if has session and is admin
|
||||
var users []models.User
|
||||
db := c.GetGorm()
|
||||
db.Find(&users)
|
||||
|
@ -46,6 +69,10 @@ func AdminUsersList(c *core.Context) *core.Response {
|
|||
Value: "Email",
|
||||
ValueType: "string",
|
||||
},
|
||||
{
|
||||
Value: "Roles",
|
||||
ValueType: "string",
|
||||
},
|
||||
{
|
||||
Value: "Created",
|
||||
},
|
||||
|
@ -57,13 +84,26 @@ func AdminUsersList(c *core.Context) *core.Response {
|
|||
},
|
||||
}
|
||||
|
||||
var listroles string
|
||||
rows := make([][]components.ContentTableTD, len(users))
|
||||
for i, u := range users {
|
||||
|
||||
roles, _ := auth.GetUserRoles(c, u.ID)
|
||||
listroles = ""
|
||||
|
||||
for _, role := range roles {
|
||||
if listroles != "" {
|
||||
listroles += ", "
|
||||
}
|
||||
listroles += role.Name
|
||||
}
|
||||
|
||||
row := []components.ContentTableTD{
|
||||
{Value: strconv.Itoa(int(u.ID))},
|
||||
{Value: u.Name},
|
||||
{Value: u.Fullname},
|
||||
{Value: u.Email},
|
||||
{Value: listroles},
|
||||
{Value: utils.FormatUnix(u.Created)},
|
||||
{Value: utils.FormatUnix(u.Updated)},
|
||||
{Value: components.ContentHref{
|
||||
|
@ -93,10 +133,44 @@ func AdminUsersList(c *core.Context) *core.Response {
|
|||
|
||||
func AdminUsersAdd(c *core.Context) *core.Response {
|
||||
|
||||
// initiate authority
|
||||
auth := new(utils.Authority)
|
||||
var session = new(utils.SessionUser)
|
||||
// true if session is active
|
||||
hassession := session.Init(c)
|
||||
|
||||
if !hassession {
|
||||
type emptytemplate struct{}
|
||||
emptyData := emptytemplate{}
|
||||
return c.Response.Template("nopermission.html", emptyData)
|
||||
}
|
||||
|
||||
session_uid := session.GetUserID()
|
||||
// check if user has role admin
|
||||
is_admin, _ := auth.CheckUserRole(c, session_uid, "admin")
|
||||
|
||||
if !is_admin {
|
||||
type emptytemplate struct{}
|
||||
emptyData := emptytemplate{}
|
||||
return c.Response.Template("nopermission.html", emptyData)
|
||||
}
|
||||
|
||||
// check if is submit
|
||||
submit := c.GetRequestParam("submit").(string)
|
||||
|
||||
errormessages := make([]string, 0)
|
||||
var listroles []components.FormCheckboxItem
|
||||
systemroles, _ := auth.GetAllRoles(c)
|
||||
|
||||
for _, systemrole := range systemroles {
|
||||
var userrole components.FormCheckboxItem
|
||||
userrole.Label = systemrole.Name
|
||||
userrole.Name = "roles"
|
||||
userrole.Value = systemrole.Slug
|
||||
if systemrole.Slug == "authenticated" {
|
||||
userrole.IsChecked = true
|
||||
}
|
||||
listroles = append(listroles, userrole)
|
||||
}
|
||||
|
||||
name := ""
|
||||
fullname := ""
|
||||
|
@ -109,6 +183,7 @@ func AdminUsersAdd(c *core.Context) *core.Response {
|
|||
fullname = c.GetRequestParam("fullname").(string)
|
||||
email = c.GetRequestParam("email").(string)
|
||||
password = c.GetRequestParam("password").(string)
|
||||
roles := c.GetRequesForm("roles").([]string)
|
||||
|
||||
// check if email exists
|
||||
var user models.User
|
||||
|
@ -161,6 +236,11 @@ func AdminUsersAdd(c *core.Context) *core.Response {
|
|||
errormessages = append(errormessages, res.Error.Error())
|
||||
} else {
|
||||
|
||||
// assign roles
|
||||
for _, role := range roles {
|
||||
auth.AssignRoleToUser(c, user.ID, role)
|
||||
}
|
||||
|
||||
// fire user registered event
|
||||
err := c.GetEventsManager().Fire(&core.Event{Name: events.USER_REGISTERED, Payload: map[string]interface{}{
|
||||
"user": user,
|
||||
|
@ -180,6 +260,7 @@ func AdminUsersAdd(c *core.Context) *core.Response {
|
|||
FieldName components.FormInput
|
||||
FieldFullname components.FormInput
|
||||
FieldEmail components.FormInput
|
||||
FieldRoles components.FormCheckbox
|
||||
FieldPassword components.FormInput
|
||||
ErrorMessages []string
|
||||
SubmitButton components.FormButton
|
||||
|
@ -208,6 +289,10 @@ func AdminUsersAdd(c *core.Context) *core.Response {
|
|||
//Autocomplete: true,
|
||||
IsRequired: true,
|
||||
},
|
||||
FieldRoles: components.FormCheckbox{
|
||||
Label: "Roles",
|
||||
AllCheckbox: listroles,
|
||||
},
|
||||
FieldPassword: components.FormInput{
|
||||
ID: "password",
|
||||
Label: "Password",
|
||||
|
@ -236,6 +321,29 @@ func AdminUsersEdit(c *core.Context) *core.Response {
|
|||
user_id := c.GetPathParam("id")
|
||||
|
||||
errormessages := make([]string, 0)
|
||||
// initiate authority
|
||||
auth := new(utils.Authority)
|
||||
|
||||
var listroles []components.FormCheckboxItem
|
||||
|
||||
systemroles, _ := auth.GetAllRoles(c)
|
||||
user_id_uint, _ := strconv.ParseUint(user_id.(string), 10, 32)
|
||||
|
||||
userroles, _ := auth.GetUserRoles(c, uint(user_id_uint))
|
||||
|
||||
for _, systemrole := range systemroles {
|
||||
var userrole components.FormCheckboxItem
|
||||
userrole.Label = systemrole.Name
|
||||
userrole.Name = "roles"
|
||||
userrole.Value = systemrole.Slug
|
||||
for _, ur := range userroles {
|
||||
if ur.Slug == systemrole.Slug {
|
||||
userrole.IsChecked = true
|
||||
break
|
||||
}
|
||||
}
|
||||
listroles = append(listroles, userrole)
|
||||
}
|
||||
|
||||
var origin_user models.User
|
||||
|
||||
|
@ -259,6 +367,7 @@ func AdminUsersEdit(c *core.Context) *core.Response {
|
|||
fullname = c.GetRequestParam("fullname").(string)
|
||||
email = c.GetRequestParam("email").(string)
|
||||
password = c.GetRequestParam("password").(string)
|
||||
roles := c.GetRequesForm("roles").([]string)
|
||||
key := c.GetRequestParam("key")
|
||||
|
||||
// check if email exists
|
||||
|
@ -316,6 +425,14 @@ func AdminUsersEdit(c *core.Context) *core.Response {
|
|||
c.GetLogger().Error("Admin user: error updating")
|
||||
errormessages = append(errormessages, fmt.Sprintf("Error updating user %s:", user_id_string))
|
||||
} else {
|
||||
|
||||
// delete roles
|
||||
auth.RevokeAllUserRole(c, origin_user.ID)
|
||||
// assign roles
|
||||
for _, role := range roles {
|
||||
auth.AssignRoleToUser(c, origin_user.ID, role)
|
||||
}
|
||||
|
||||
return c.Response.Redirect("/admin/users")
|
||||
}
|
||||
}
|
||||
|
@ -325,6 +442,7 @@ func AdminUsersEdit(c *core.Context) *core.Response {
|
|||
FieldName components.FormInput
|
||||
FieldFullname components.FormInput
|
||||
FieldEmail components.FormInput
|
||||
FieldRoles components.FormCheckbox
|
||||
FieldPassword components.FormInput
|
||||
FieldKey components.FormInput
|
||||
ErrorMessages []string
|
||||
|
@ -355,6 +473,10 @@ func AdminUsersEdit(c *core.Context) *core.Response {
|
|||
//Autocomplete: true,
|
||||
IsRequired: true,
|
||||
},
|
||||
FieldRoles: components.FormCheckbox{
|
||||
Label: "Roles",
|
||||
AllCheckbox: listroles,
|
||||
},
|
||||
FieldPassword: components.FormInput{
|
||||
ID: "password",
|
||||
Label: "Password",
|
||||
|
@ -461,6 +583,9 @@ func AdminUsersDelConfirm(c *core.Context) *core.Response {
|
|||
// check if is the seed user
|
||||
seed := "1"
|
||||
if user_id != seed {
|
||||
|
||||
// initiate authority
|
||||
auth := new(utils.Authority)
|
||||
// Delete the user
|
||||
// fire user delete event
|
||||
err := c.GetEventsManager().Fire(&core.Event{Name: events.USER_DELETED, Payload: map[string]interface{}{
|
||||
|
@ -469,6 +594,7 @@ func AdminUsersDelConfirm(c *core.Context) *core.Response {
|
|||
if err != nil {
|
||||
c.GetLogger().Error(err.Error())
|
||||
}
|
||||
auth.RevokeAllUserRole(c, origin_user.ID)
|
||||
result_db.Unscoped().Delete(&origin_user)
|
||||
}
|
||||
}
|
||||
|
|
202
controllers/pdfsample.go
Normal file
202
controllers/pdfsample.go
Normal file
|
@ -0,0 +1,202 @@
|
|||
// Copyright (c) 2024 Jose Cely <jose.cely@gmail.com>
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"git.smarteching.com/goffee/core"
|
||||
// for graphs
|
||||
"git.smarteching.com/zeni/go-chart/v2"
|
||||
// for PDF
|
||||
"github.com/jung-kurt/gofpdf"
|
||||
// for xslx
|
||||
"github.com/xuri/excelize/v2"
|
||||
)
|
||||
|
||||
// sample buffer back, pdf
|
||||
func Themepdf(c *core.Context) *core.Response {
|
||||
|
||||
var b bytes.Buffer
|
||||
pw := io.Writer(&b)
|
||||
//pr := io.Reader(&b)
|
||||
|
||||
marginX := 10.0
|
||||
marginY := 20.0
|
||||
|
||||
pdf := gofpdf.New("P", "mm", "A4", "")
|
||||
pdf.AddPage()
|
||||
pdf.SetFont("Arial", "B", 16)
|
||||
|
||||
pdf.ImageOptions("storage/public/img/gopher_read.png", marginY, marginX, 25, 25, false, gofpdf.ImageOptions{ImageType: "PNG", ReadDpi: true}, 0, "")
|
||||
|
||||
pdf.SetXY(140, marginX)
|
||||
pdf.SetFont("Arial", "B", 16)
|
||||
|
||||
//_, lineHeight := pdf.GetFontSize()
|
||||
|
||||
t := time.Now()
|
||||
dateStr := t.Format("2006-01-02 15:04")
|
||||
|
||||
pdf.Cell(0, 10, "Report XYZ")
|
||||
pdf.SetXY(140, 20)
|
||||
pdf.SetFont("Arial", "B", 12)
|
||||
pdf.Cell(0, 10, dateStr)
|
||||
|
||||
// Draw the table
|
||||
|
||||
pdf.SetXY(marginX, 50)
|
||||
|
||||
lineHt := 8.0
|
||||
|
||||
const colNumber = 2
|
||||
header := [colNumber]string{"No", "Description"}
|
||||
colWidth := [colNumber]float64{20.0, 70}
|
||||
|
||||
// Headers
|
||||
pdf.SetFont("Arial", "B", 12)
|
||||
|
||||
pdf.SetFillColor(200, 200, 200)
|
||||
for colJ := 0; colJ < colNumber; colJ++ {
|
||||
pdf.CellFormat(colWidth[colJ], lineHt, header[colJ], "1", 0, "CM", true, 0, "")
|
||||
}
|
||||
|
||||
pdf.Ln(-1)
|
||||
pdf.SetFillColor(255, 255, 255)
|
||||
|
||||
// Table data
|
||||
pdf.SetFontStyle("")
|
||||
|
||||
contents := getContents()
|
||||
|
||||
for _, content := range contents {
|
||||
pdf.CellFormat(colWidth[0], lineHt, content[0], "1", 0, "CM", true, 0, "")
|
||||
pdf.CellFormat(colWidth[1], lineHt, content[1], "1", 0, "LM", true, 0, "")
|
||||
pdf.Ln(-1)
|
||||
}
|
||||
|
||||
chartpng := getImageBuffer()
|
||||
//r := bytes.NewReader(chartpng)
|
||||
|
||||
//httpimg.Register(pdf, url, "")
|
||||
//pdf.Image(url, 110, 50, 80, 80, false, "", 0, "")
|
||||
//pdf.ImageOptions("storage/public/img/biplane.jpg", 110, 50, 80, 80, false, gofpdf.ImageOptions{ImageType: "JPEG", ReadDpi: true}, 0, "")
|
||||
|
||||
pdf.RegisterImageOptionsReader("pcart.png", gofpdf.ImageOptions{ImageType: "PNG", ReadDpi: true}, chartpng)
|
||||
pdf.ImageOptions("pcart.png", 110, 50, 80, 80, false, gofpdf.ImageOptions{ImageType: "JPEG", ReadDpi: true}, 0, "")
|
||||
|
||||
pdf.Ln(lineHt)
|
||||
pdf.Ln(lineHt)
|
||||
|
||||
pdf.SetFont("Arial", "", 12)
|
||||
_, lineHeight := pdf.GetFontSize()
|
||||
|
||||
pdf.Cell(0, lineHeight, "At vero eos et accusamus et iusto odio dignissimos dntium voluptatum deleniti atqu.")
|
||||
|
||||
// generate PDF
|
||||
//pdf.OutputFileAndClose("hello.pdf")
|
||||
|
||||
err := pdf.Output(pw)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
|
||||
}
|
||||
|
||||
return c.Response.BufferFile("elreport.pdf", "application/pdf", b)
|
||||
|
||||
}
|
||||
|
||||
func Themexslx(c *core.Context) *core.Response {
|
||||
|
||||
var b bytes.Buffer
|
||||
|
||||
f := excelize.NewFile()
|
||||
|
||||
defer func() {
|
||||
if err := f.Close(); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}()
|
||||
|
||||
for idx, row := range [][]interface{}{
|
||||
{nil, "Cerveza", "Vodka", "Ron"}, {"Small", 2, 3, 3},
|
||||
{"Normal", 5, 2, 4}, {"Large", 6, 7, 8},
|
||||
} {
|
||||
cell, err := excelize.CoordinatesToCellName(1, idx+1)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
f.SetSheetRow("Sheet1", cell, &row)
|
||||
}
|
||||
|
||||
if err := f.AddChart("Sheet1", "E1", &excelize.Chart{
|
||||
Type: excelize.Pie,
|
||||
Series: []excelize.ChartSeries{
|
||||
{
|
||||
Name: "Totales",
|
||||
Categories: "Sheet1!$B$1:$D$1",
|
||||
Values: "Sheet1!$B$2:$D$2",
|
||||
},
|
||||
},
|
||||
Format: excelize.GraphicOptions{
|
||||
OffsetX: 15,
|
||||
OffsetY: 10,
|
||||
},
|
||||
Title: []excelize.RichTextRun{
|
||||
{
|
||||
Text: "Fruit Pie Chart",
|
||||
},
|
||||
},
|
||||
PlotArea: excelize.ChartPlotArea{
|
||||
ShowPercent: true,
|
||||
},
|
||||
}); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
buf, err := f.WriteToBuffer()
|
||||
if err != nil {
|
||||
fmt.Fprint(buf, err.Error())
|
||||
}
|
||||
|
||||
// Get byte array
|
||||
b.ReadFrom(buf)
|
||||
|
||||
return c.Response.BufferFile("elreport.xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", b)
|
||||
}
|
||||
|
||||
func getContents() [][]string {
|
||||
return [][]string{
|
||||
{"node1", "Swamp"},
|
||||
{"lore1", "Sorin, A Planeswalker"},
|
||||
{"dia1", "Tassa"},
|
||||
{"ter3", "Skinrender"},
|
||||
{"bgt5", "Island"},
|
||||
{"weww", "Mountain"},
|
||||
{"asda", "Plain"},
|
||||
{"tetra", "Time Walk"},
|
||||
}
|
||||
}
|
||||
|
||||
func getImageBuffer() *bytes.Buffer {
|
||||
pie := chart.PieChart{
|
||||
Width: 512,
|
||||
Height: 512,
|
||||
Values: []chart.Value{
|
||||
{Value: 8, Label: "Blue"},
|
||||
{Value: 5, Label: "Green"},
|
||||
{Value: 2, Label: "Gray"},
|
||||
{Value: 2, Label: "Orange"},
|
||||
},
|
||||
}
|
||||
|
||||
buffer := bytes.NewBuffer([]byte{})
|
||||
pie.Render(chart.PNG, buffer)
|
||||
|
||||
return buffer
|
||||
}
|
16
models/permission.go
Normal file
16
models/permission.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) 2025 Zeni Kim <zenik@smarteching.com>
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
|
||||
type Permission struct {
|
||||
BaseModel
|
||||
Name string
|
||||
Slug string
|
||||
}
|
||||
|
||||
// TableName sets the table name
|
||||
func (Permission) TableName() string {
|
||||
return "permissions"
|
||||
}
|
16
models/role-permissions.go
Normal file
16
models/role-permissions.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) 2025 Zeni Kim <zenik@smarteching.com>
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
|
||||
type RolePermission struct {
|
||||
BaseModel
|
||||
RoleID uint // Role id
|
||||
PermissionID uint // Permission id
|
||||
}
|
||||
|
||||
// TableName sets the table name
|
||||
func (RolePermission) TableName() string {
|
||||
return "role_permissions"
|
||||
}
|
16
models/roles.go
Normal file
16
models/roles.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) 2025 Zeni Kim <zenik@smarteching.com>
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
|
||||
type Role struct {
|
||||
BaseModel
|
||||
Name string // The name of the role
|
||||
Slug string // String based unique identifier of the role, (use hyphen seperated role name '-', instead of space)
|
||||
}
|
||||
|
||||
// TableName sets the table name
|
||||
func (Role) TableName() string {
|
||||
return "roles"
|
||||
}
|
16
models/user-roles.go
Normal file
16
models/user-roles.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) 2025 Zeni Kim <zenik@smarteching.com>
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package models
|
||||
|
||||
type UserRole struct {
|
||||
BaseModel
|
||||
UserID uint // The user id
|
||||
RoleID uint // The role id
|
||||
}
|
||||
|
||||
// TableName sets the table name
|
||||
func (UserRole) TableName() string {
|
||||
return "user_roles"
|
||||
}
|
|
@ -27,6 +27,8 @@ func registerRoutes() {
|
|||
controller.Get("/themepanel", controllers.Themedemo)
|
||||
controller.Get("/themeelements", controllers.ThemeElements)
|
||||
|
||||
controller.Get("/themepdf", controllers.Themepdf)
|
||||
|
||||
// Uncomment the lines below to enable authentication
|
||||
controller.Post("/signup", controllers.Signup)
|
||||
controller.Post("/signin", controllers.Signin)
|
||||
|
|
|
@ -6,8 +6,12 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"git.smarteching.com/goffee/core"
|
||||
"git.smarteching.com/goffee/cup/models"
|
||||
"git.smarteching.com/goffee/cup/utils"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func RunAutoMigrations() {
|
||||
|
@ -17,5 +21,18 @@ func RunAutoMigrations() {
|
|||
//##############################
|
||||
|
||||
// Add auto migrations for your models here...
|
||||
db.AutoMigrate(&models.User{})
|
||||
db.AutoMigrate(&models.UserRole{})
|
||||
db.AutoMigrate(&models.Role{})
|
||||
db.AutoMigrate(&models.RolePermission{})
|
||||
db.AutoMigrate(&models.Permission{})
|
||||
|
||||
// End your auto migrations
|
||||
|
||||
// Create seed data data, DO NOT TOUCH
|
||||
if err := db.AutoMigrate(&models.User{}); err == nil && db.Migrator().HasTable(&models.User{}) {
|
||||
if err := db.First(&models.User{}).Error; errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
utils.CreateSeedData()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
{{template "form_input" .FieldName}}
|
||||
{{template "form_input" .FieldFullname}}
|
||||
{{template "form_input" .FieldEmail}}
|
||||
{{template "form_checkbox" .FieldRoles}}
|
||||
{{template "form_input" .FieldPassword}}
|
||||
<hr>
|
||||
{{template "form_button" .SubmitButton}}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
{{template "form_input" .FieldName}}
|
||||
{{template "form_input" .FieldFullname}}
|
||||
{{template "form_input" .FieldEmail}}
|
||||
{{template "form_checkbox" .FieldRoles}}
|
||||
{{template "form_input" .FieldPassword}}
|
||||
{{template "form_input" .FieldKey}}
|
||||
{{template "form_button" .SubmitButton}}
|
||||
|
|
12
storage/templates/nopermission.html
Normal file
12
storage/templates/nopermission.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
{{template "page_head" "No permission"}}
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
You do not have permission to visit this page.
|
||||
</div>
|
||||
</div>
|
||||
{{template "page_footer"}}
|
||||
</body>
|
||||
</html>
|
436
utils/authority.go
Normal file
436
utils/authority.go
Normal file
|
@ -0,0 +1,436 @@
|
|||
// Copyright (c) 2025 Zeni Kim <zenik@smarteching.com>
|
||||
// Use of this source code is governed by MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.smarteching.com/goffee/core"
|
||||
"git.smarteching.com/goffee/cup/models"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Authority struct{}
|
||||
|
||||
var auth *Authority
|
||||
|
||||
var (
|
||||
ErrPermissionInUse = errors.New("cannot delete assigned permission")
|
||||
ErrPermissionNotFound = errors.New("permission not found")
|
||||
ErrRoleInUse = errors.New("cannot delete assigned role")
|
||||
ErrRoleNotFound = errors.New("role not found")
|
||||
)
|
||||
|
||||
// Add a new role to the database
|
||||
func (a *Authority) CreateRole(c *core.Context, r models.Role) error {
|
||||
roleSlug := r.Slug
|
||||
var dbRole models.Role
|
||||
res := c.GetGorm().Where("slug = ?", roleSlug).First(&dbRole)
|
||||
if res.Error != nil {
|
||||
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
// create
|
||||
createRes := c.GetGorm().Create(&r)
|
||||
if createRes.Error != nil {
|
||||
return createRes.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return res.Error
|
||||
}
|
||||
|
||||
return errors.New(fmt.Sprintf("role '%v' already exists", roleSlug))
|
||||
}
|
||||
|
||||
// Add a new permission to the database
|
||||
func (a *Authority) CreatePermission(c *core.Context, p models.Permission) error {
|
||||
permSlug := p.Slug
|
||||
var dbPerm models.Permission
|
||||
res := c.GetGorm().Where("slug = ?", permSlug).First(&dbPerm)
|
||||
if res.Error != nil {
|
||||
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
// create
|
||||
createRes := c.GetGorm().Create(&p)
|
||||
if createRes.Error != nil {
|
||||
return createRes.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return res.Error
|
||||
}
|
||||
|
||||
return errors.New(fmt.Sprintf("permission '%v' already exists", permSlug))
|
||||
}
|
||||
|
||||
// Assigns a group of permissions to a given role
|
||||
func (a *Authority) AssignPermissionsToRole(c *core.Context, roleSlug string, permSlugs []string) error {
|
||||
var role models.Role
|
||||
rRes := c.GetGorm().Where("slug = ?", roleSlug).First(&role)
|
||||
if rRes.Error != nil {
|
||||
if errors.Is(rRes.Error, gorm.ErrRecordNotFound) {
|
||||
return ErrRoleNotFound
|
||||
}
|
||||
return rRes.Error
|
||||
}
|
||||
var perms []models.Permission
|
||||
for _, permSlug := range permSlugs {
|
||||
var perm models.Permission
|
||||
pRes := c.GetGorm().Where("slug = ?", permSlug).First(&perm)
|
||||
if pRes.Error != nil {
|
||||
if errors.Is(pRes.Error, gorm.ErrRecordNotFound) {
|
||||
return ErrPermissionNotFound
|
||||
}
|
||||
return pRes.Error
|
||||
}
|
||||
perms = append(perms, perm)
|
||||
}
|
||||
tx := c.GetGorm().Begin()
|
||||
for _, perm := range perms {
|
||||
var rolePerm models.RolePermission
|
||||
res := c.GetGorm().Where("role_id = ?", role.ID).Where("permission_id =?", perm.ID).First(&rolePerm)
|
||||
if res.Error != nil && errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
cRes := tx.Create(&models.RolePermission{RoleID: role.ID, PermissionID: perm.ID})
|
||||
if cRes.Error != nil {
|
||||
tx.Rollback()
|
||||
return cRes.Error
|
||||
}
|
||||
}
|
||||
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
tx.Rollback()
|
||||
return res.Error
|
||||
}
|
||||
if rolePerm != (models.RolePermission{}) {
|
||||
tx.Rollback()
|
||||
return errors.New(fmt.Sprintf("permission '%v' is aleady assigned to the role '%v'", perm.Name, role.Name))
|
||||
}
|
||||
rolePerm = models.RolePermission{}
|
||||
}
|
||||
return tx.Commit().Error
|
||||
}
|
||||
|
||||
// Assigns a role to a given user
|
||||
func (a *Authority) AssignRoleToUser(c *core.Context, userID uint, roleSlug string) error {
|
||||
var role models.Role
|
||||
res := c.GetGorm().Where("slug = ?", roleSlug).First(&role)
|
||||
if res.Error != nil {
|
||||
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
return ErrRoleNotFound
|
||||
}
|
||||
return res.Error
|
||||
}
|
||||
var userRole models.UserRole
|
||||
res = c.GetGorm().Where("user_id = ?", userID).Where("role_id = ?", role.ID).First(&userRole)
|
||||
if res.Error != nil && errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
c.GetGorm().Create(&models.UserRole{UserID: userID, RoleID: role.ID})
|
||||
return nil
|
||||
}
|
||||
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
return res.Error
|
||||
}
|
||||
|
||||
return errors.New(fmt.Sprintf("this role '%v' is aleady assigned to the user", roleSlug))
|
||||
}
|
||||
|
||||
// Checks if a role is assigned to a user
|
||||
func (a *Authority) CheckUserRole(c *core.Context, userID uint, roleSlug string) (bool, error) {
|
||||
// find the role
|
||||
var role models.Role
|
||||
res := c.GetGorm().Where("slug = ?", roleSlug).First(&role)
|
||||
if res.Error != nil {
|
||||
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
return false, ErrRoleNotFound
|
||||
}
|
||||
return false, res.Error
|
||||
}
|
||||
|
||||
// check if the role is a assigned
|
||||
var userRole models.UserRole
|
||||
res = c.GetGorm().Where("user_id = ?", userID).Where("role_id = ?", role.ID).First(&userRole)
|
||||
if res.Error != nil {
|
||||
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
return false, nil
|
||||
}
|
||||
return false, res.Error
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Checks if a permission is assigned to a user
|
||||
func (a *Authority) CheckUserPermission(c *core.Context, userID uint, permSlug string) (bool, error) {
|
||||
// the user role
|
||||
var userRoles []models.UserRole
|
||||
res := c.GetGorm().Where("user_id = ?", userID).Find(&userRoles)
|
||||
if res.Error != nil {
|
||||
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
return false, nil
|
||||
}
|
||||
return false, res.Error
|
||||
}
|
||||
|
||||
//prepare an array of role ids
|
||||
var roleIDs []interface{}
|
||||
for _, r := range userRoles {
|
||||
roleIDs = append(roleIDs, r.RoleID)
|
||||
}
|
||||
|
||||
// find the permission
|
||||
var perm models.Permission
|
||||
res = c.GetGorm().Where("slug = ?", permSlug).First(&perm)
|
||||
if res.Error != nil {
|
||||
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
return false, ErrPermissionNotFound
|
||||
}
|
||||
return false, res.Error
|
||||
}
|
||||
|
||||
// find the role permission
|
||||
var rolePermission models.RolePermission
|
||||
res = c.GetGorm().Where("role_id IN (?)", roleIDs).Where("permission_id = ?", perm.ID).First(&rolePermission)
|
||||
if res.Error != nil && errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
return false, nil
|
||||
}
|
||||
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
return false, res.Error
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Checks if a permission is assigned to a role
|
||||
func (a *Authority) CheckRolePermission(c *core.Context, roleSlug string, permSlug string) (bool, error) {
|
||||
// find the role
|
||||
var role models.Role
|
||||
res := c.GetGorm().Where("slug = ?", roleSlug).First(&role)
|
||||
if res.Error != nil {
|
||||
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
return false, ErrRoleNotFound
|
||||
}
|
||||
return false, res.Error
|
||||
}
|
||||
|
||||
// find the permission
|
||||
var perm models.Permission
|
||||
res = c.GetGorm().Where("slug = ?", permSlug).First(&perm)
|
||||
if res.Error != nil {
|
||||
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
return false, ErrPermissionNotFound
|
||||
}
|
||||
return false, res.Error
|
||||
}
|
||||
|
||||
// find the rolePermission
|
||||
var rolePermission models.RolePermission
|
||||
res = c.GetGorm().Where("role_id = ?", role.ID).Where("permission_id = ?", perm.ID).First(&rolePermission)
|
||||
if res.Error != nil {
|
||||
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
return false, nil
|
||||
}
|
||||
return false, res.Error
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Revokes a roles's permission
|
||||
func (a *Authority) RevokeRolePermission(c *core.Context, roleSlug string, permSlug string) error {
|
||||
// find the role
|
||||
var role models.Role
|
||||
res := c.GetGorm().Where("slug = ?", roleSlug).First(&role)
|
||||
if res.Error != nil {
|
||||
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
return ErrRoleNotFound
|
||||
}
|
||||
return res.Error
|
||||
}
|
||||
|
||||
// find the permission
|
||||
var perm models.Permission
|
||||
res = c.GetGorm().Where("slug = ?", permSlug).First(&perm)
|
||||
if res.Error != nil {
|
||||
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
return ErrPermissionNotFound
|
||||
}
|
||||
return res.Error
|
||||
}
|
||||
|
||||
// revoke the permission
|
||||
rRes := c.GetGorm().Where("role_id = ?", role.ID).Where("permission_id = ?", perm.ID).Delete(models.RolePermission{})
|
||||
if rRes.Error != nil {
|
||||
return rRes.Error
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Revokes a user's role
|
||||
func (a *Authority) RevokeUserRole(c *core.Context, userID uint, roleSlug string) error {
|
||||
// find the role
|
||||
var role models.Role
|
||||
res := c.GetGorm().Where("slug = ?", roleSlug).First(&role)
|
||||
if res.Error != nil {
|
||||
if errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
return ErrRoleNotFound
|
||||
}
|
||||
return res.Error
|
||||
}
|
||||
|
||||
// revoke the role
|
||||
rRes := c.GetGorm().Where("user_id = ?", userID).Where("role_id = ?", role.ID).Delete(models.UserRole{})
|
||||
if rRes.Error != nil {
|
||||
return rRes.Error
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Revokes all user's role
|
||||
func (a *Authority) RevokeAllUserRole(c *core.Context, userID uint) error {
|
||||
// revoke the role
|
||||
rRes := c.GetGorm().Where("user_id = ?", userID).Delete(models.UserRole{})
|
||||
if rRes.Error != nil {
|
||||
return rRes.Error
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns all stored roles
|
||||
func (a *Authority) GetAllRoles(c *core.Context) ([]models.Role, error) {
|
||||
var roles []models.Role
|
||||
res := c.GetGorm().Find(&roles)
|
||||
if res.Error != nil {
|
||||
return nil, res.Error
|
||||
}
|
||||
|
||||
return roles, nil
|
||||
}
|
||||
|
||||
// Returns all user assigned roles
|
||||
func (a *Authority) GetUserRoles(c *core.Context, userID uint) ([]models.Role, error) {
|
||||
var userRoles []models.UserRole
|
||||
res := c.GetGorm().Where("user_id = ?", userID).Find(&userRoles)
|
||||
if res.Error != nil {
|
||||
return nil, res.Error
|
||||
}
|
||||
|
||||
var roleIDs []interface{}
|
||||
for _, r := range userRoles {
|
||||
roleIDs = append(roleIDs, r.RoleID)
|
||||
}
|
||||
|
||||
var roles []models.Role
|
||||
res = c.GetGorm().Where("id IN (?)", roleIDs).Find(&roles)
|
||||
if res.Error != nil {
|
||||
return nil, res.Error
|
||||
}
|
||||
|
||||
return roles, nil
|
||||
}
|
||||
|
||||
// Returns all role assigned permissions
|
||||
func (a *Authority) GetRolePermissions(c *core.Context, roleSlug string) ([]models.Permission, error) {
|
||||
var role models.Role
|
||||
res := c.GetGorm().Where("slug = ?", roleSlug).Find(&role)
|
||||
if res.Error != nil {
|
||||
return nil, res.Error
|
||||
}
|
||||
|
||||
var rolePerms []models.RolePermission
|
||||
res = c.GetGorm().Where("role_id = ?", role.ID).Find(&rolePerms)
|
||||
if res.Error != nil {
|
||||
return nil, res.Error
|
||||
}
|
||||
var permIDs []interface{}
|
||||
for _, rolePerm := range rolePerms {
|
||||
permIDs = append(permIDs, rolePerm.PermissionID)
|
||||
}
|
||||
|
||||
var perms []models.Permission
|
||||
res = c.GetGorm().Where("id IN (?)", permIDs).Find(&perms)
|
||||
if res.Error != nil {
|
||||
return nil, res.Error
|
||||
}
|
||||
|
||||
return perms, nil
|
||||
}
|
||||
|
||||
// Returns all stored permissions
|
||||
func (a *Authority) GetAllPermissions(c *core.Context) ([]models.Permission, error) {
|
||||
var perms []models.Permission
|
||||
res := c.GetGorm().Find(&perms)
|
||||
if res.Error != nil {
|
||||
return nil, res.Error
|
||||
}
|
||||
|
||||
return perms, nil
|
||||
}
|
||||
|
||||
// Deletes a given role even if it's has assigned permissions
|
||||
func (a *Authority) DeleteRole(c *core.Context, roleSlug string) error {
|
||||
// find the role
|
||||
var role models.Role
|
||||
res := c.GetGorm().Where("slug = ?", roleSlug).First(&role)
|
||||
if res.Error != nil {
|
||||
return res.Error
|
||||
}
|
||||
|
||||
// check if the role is assigned to a user
|
||||
var ca int64
|
||||
res = c.GetGorm().Model(models.UserRole{}).Where("role_id = ?", role.ID).Count(&ca)
|
||||
if res.Error != nil {
|
||||
return res.Error
|
||||
}
|
||||
|
||||
if ca != 0 {
|
||||
// role is assigned
|
||||
return ErrRoleInUse
|
||||
}
|
||||
tx := c.GetGorm().Begin()
|
||||
// revoke the assignment of permissions before deleting the role
|
||||
dRes := tx.Where("role_id = ?", role.ID).Delete(models.RolePermission{})
|
||||
if dRes.Error != nil {
|
||||
tx.Rollback()
|
||||
return dRes.Error
|
||||
}
|
||||
|
||||
// delete the role
|
||||
dRes = c.GetGorm().Where("slug = ?", roleSlug).Delete(models.Role{})
|
||||
if dRes.Error != nil {
|
||||
tx.Rollback()
|
||||
return dRes.Error
|
||||
}
|
||||
|
||||
return tx.Commit().Error
|
||||
}
|
||||
|
||||
// Deletes a given permission
|
||||
func (a *Authority) DeletePermission(c *core.Context, permSlug string) error {
|
||||
// find the permission
|
||||
var perm models.Permission
|
||||
res := c.GetGorm().Where("slug = ?", permSlug).First(&perm)
|
||||
if res.Error != nil {
|
||||
return res.Error
|
||||
}
|
||||
|
||||
// check if the permission is assigned to a role
|
||||
var rolePermission models.RolePermission
|
||||
res = c.GetGorm().Where("permission_id = ?", perm.ID).First(&rolePermission)
|
||||
if res.Error != nil && !errors.Is(res.Error, gorm.ErrRecordNotFound) {
|
||||
return res.Error
|
||||
}
|
||||
|
||||
if res.Error == nil {
|
||||
return ErrPermissionInUse
|
||||
}
|
||||
|
||||
// delete the permission
|
||||
dRes := c.GetGorm().Where("slug = ?", permSlug).Delete(models.Permission{})
|
||||
if dRes.Error != nil {
|
||||
return dRes.Error
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -8,9 +8,69 @@ package utils
|
|||
import (
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"git.smarteching.com/goffee/core"
|
||||
"git.smarteching.com/goffee/cup/models"
|
||||
)
|
||||
|
||||
func CreateSeedData() {
|
||||
|
||||
db := core.ResolveGorm()
|
||||
var hashing = new(core.Hashing)
|
||||
var role models.Role
|
||||
|
||||
// seed user
|
||||
password := "goffee"
|
||||
name := "admin"
|
||||
fullname := "Goffee administrator"
|
||||
email := "change@me.com"
|
||||
passwordHashed, _ := hashing.HashPassword(password)
|
||||
|
||||
user := models.User{
|
||||
Name: name,
|
||||
Fullname: fullname,
|
||||
Email: email,
|
||||
Password: passwordHashed,
|
||||
}
|
||||
result := db.Create(&user)
|
||||
if result.Error != nil {
|
||||
log.Fatal("Can't create seed user in database")
|
||||
}
|
||||
// seed roles
|
||||
roles := []models.Role{
|
||||
{Name: "Administrator", Slug: "admin"},
|
||||
{Name: "Authenticated", Slug: "authenticated"},
|
||||
}
|
||||
|
||||
for _, role := range roles {
|
||||
result = db.Create(&role)
|
||||
if result.Error != nil {
|
||||
log.Fatal("Can't create seed role in database")
|
||||
}
|
||||
}
|
||||
|
||||
// seed permission
|
||||
permission := models.Permission{Name: "Users administration", Slug: "admin-users"}
|
||||
result = db.Create(&permission)
|
||||
if result.Error != nil {
|
||||
log.Fatal("Can't create seed permission in database")
|
||||
}
|
||||
result = db.Where("slug = ?", "admin").First(&role)
|
||||
if result.Error != nil {
|
||||
log.Fatal("Can't find user admin in database")
|
||||
}
|
||||
result = db.Create(&models.RolePermission{RoleID: role.ID, PermissionID: permission.ID})
|
||||
if result.Error != nil {
|
||||
log.Fatal("Can't register permission role in database")
|
||||
}
|
||||
result = db.Create(&models.UserRole{UserID: user.ID, RoleID: role.ID})
|
||||
if result.Error != nil {
|
||||
log.Fatal("Can't assign role administrator to user in database")
|
||||
}
|
||||
}
|
||||
|
||||
// generate a hashed string to be used as key for caching auth jwt token
|
||||
func CreateAuthTokenHashedCacheKey(userID uint, userAgent string) string {
|
||||
cacheKey := fmt.Sprintf("userid:_%v_useragent:_%v_jwt_token", userID, userAgent)
|
||||
|
|
Loading…
Reference in a new issue