update
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
package models
|
||||
|
||||
type App struct {
|
||||
BaseModel
|
||||
Name string `json:"name"`
|
||||
UUID string `json:"uuid"`
|
||||
Host string `json:"host"`
|
||||
WxID string `json:"wx_id" gorm:""`
|
||||
Wx *Wechat `json:"wx" gorm:"association_foreignkey:ID"`
|
||||
}
|
||||
|
||||
type Wechat struct {
|
||||
BaseModel
|
||||
// 网页授权登录用
|
||||
WxID string `json:"wx_id"`
|
||||
AgentID string `json:"agent_id"`
|
||||
Url string `json:"url"`
|
||||
|
||||
// 获取access_token用
|
||||
CorpID string `json:"corp_id"`
|
||||
CorpSecret string `json:"corp_secret"`
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"OneAuth/cfg"
|
||||
"bytes"
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
type JSON []byte
|
||||
|
||||
func (j JSON) Value() (driver.Value, error) {
|
||||
if j.IsNull() {
|
||||
return nil, nil
|
||||
}
|
||||
return string(j), nil
|
||||
}
|
||||
func (j *JSON) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
*j = nil
|
||||
return nil
|
||||
}
|
||||
s, ok := value.([]byte)
|
||||
if !ok {
|
||||
return errors.New("invalid scan source")
|
||||
}
|
||||
*j = append((*j)[0:0], s...)
|
||||
return nil
|
||||
}
|
||||
func (j JSON) MarshalJSON() ([]byte, error) {
|
||||
if j == nil {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
return j, nil
|
||||
}
|
||||
func (j *JSON) UnmarshalJSON(data []byte) error {
|
||||
if j == nil {
|
||||
return errors.New("null point exception")
|
||||
}
|
||||
*j = append((*j)[0:0], data...)
|
||||
return nil
|
||||
}
|
||||
func (j JSON) IsNull() bool {
|
||||
return len(j) == 0 || string(j) == "null"
|
||||
}
|
||||
func (j JSON) Equals(j1 JSON) bool {
|
||||
return bytes.Equal(j, j1)
|
||||
}
|
||||
|
||||
// JSONTime custom json time
|
||||
type JSONTime struct {
|
||||
time.Time
|
||||
}
|
||||
|
||||
func Now() *JSONTime {
|
||||
return &JSONTime{time.Now()}
|
||||
}
|
||||
|
||||
// MarshalJSON 实现它的json序列化方法
|
||||
func (jt JSONTime) MarshalJSON() ([]byte, error) {
|
||||
var stamp = fmt.Sprintf("\"%s\"", jt.Format(cfg.CFG.TimeFormat))
|
||||
return []byte(stamp), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON 反序列化方法
|
||||
func (jt *JSONTime) UnmarshalJSON(data []byte) (err error) {
|
||||
now, err := time.ParseInLocation(`"`+cfg.CFG.TimeFormat+`"`, string(data), time.Local)
|
||||
*jt = JSONTime{now}
|
||||
return
|
||||
}
|
||||
|
||||
// Value insert timestamp into mysql need this function.
|
||||
func (jt JSONTime) Value() (driver.Value, error) {
|
||||
var zeroTime time.Time
|
||||
if jt.Time.UnixNano() == zeroTime.UnixNano() {
|
||||
return nil, nil
|
||||
}
|
||||
return jt.Time, nil
|
||||
}
|
||||
|
||||
// Scan value of time.Time
|
||||
func (jt *JSONTime) Scan(v interface{}) error {
|
||||
value, ok := v.(time.Time)
|
||||
if ok {
|
||||
*jt = JSONTime{Time: value}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("can not convert %v to timestamp", v)
|
||||
}
|
||||
|
||||
func (jt *JSONTime) SetTime(t time.Time) {
|
||||
jt.Time = t
|
||||
}
|
||||
|
||||
type BaseModel struct {
|
||||
ID uint `json:"id" gorm:"primary_key"`
|
||||
CreatedAt JSONTime `json:"created_at"`
|
||||
UpdatedAt JSONTime `json:"updated_at"`
|
||||
DeletedAt *JSONTime `json:"deleted_at" sql:"index"`
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"OneAuth/cfg"
|
||||
"github.com/veypi/utils/log"
|
||||
)
|
||||
|
||||
var GlobalRoles = make(map[uint]*Role)
|
||||
|
||||
func SyncGlobalRoles() {
|
||||
roles := make([]*Role, 0, 10)
|
||||
err := cfg.DB().Preload("Auths").Find(&roles).Error
|
||||
if err != nil {
|
||||
log.Warn().Msgf("sync global roles error: %s", err.Error())
|
||||
return
|
||||
}
|
||||
for _, r := range roles {
|
||||
GlobalRoles[r.ID] = r
|
||||
}
|
||||
}
|
||||
|
||||
type UserRole struct {
|
||||
BaseModel
|
||||
UserID uint `json:"user_id"`
|
||||
RoleID uint `json:"role_id"`
|
||||
}
|
||||
|
||||
type RoleAuth struct {
|
||||
BaseModel
|
||||
RoleID uint `json:"role_id"`
|
||||
AuthID uint `json:"auth_id"`
|
||||
}
|
||||
|
||||
type Role struct {
|
||||
BaseModel
|
||||
Name string `json:"name"`
|
||||
// 角色类型
|
||||
// 0: 系统角色 1: 用户角色
|
||||
Category uint `json:"category" gorm:"default:0"`
|
||||
// 角色标签
|
||||
Tag string `json:"tag" gorm:"default:''"`
|
||||
Users []*User `json:"users" gorm:"many2many:user_role;"`
|
||||
// 具体权限
|
||||
Auths []*Auth `json:"auths" gorm:"many2many:role_auth;"`
|
||||
IsUnique bool `json:"is_unique" gorm:"default:false"`
|
||||
}
|
||||
|
||||
func (r Role) CheckAuth(name string, tags ...string) AuthLevel {
|
||||
res := AuthNone
|
||||
tag := ""
|
||||
if len(tags) > 0 {
|
||||
tag = tags[0]
|
||||
}
|
||||
for _, a := range r.Auths {
|
||||
if a.Name == "admin" && a.Tag == "" || (a.Name == "admin" && a.Tag == tag) || (a.Name == name && a.Tag == tag) {
|
||||
if a.Level > res {
|
||||
res = a.Level
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
type AuthLevel uint
|
||||
|
||||
const (
|
||||
AuthNone AuthLevel = 0
|
||||
AuthRead AuthLevel = 1
|
||||
AuthCreate AuthLevel = 2
|
||||
AuthUpdate AuthLevel = 3
|
||||
AuthDelete AuthLevel = 4
|
||||
)
|
||||
|
||||
func (a AuthLevel) CanRead() bool {
|
||||
return a >= AuthRead
|
||||
}
|
||||
|
||||
func (a AuthLevel) CanCreate() bool {
|
||||
return a >= AuthCreate
|
||||
}
|
||||
|
||||
func (a AuthLevel) CanUpdate() bool {
|
||||
return a >= AuthUpdate
|
||||
}
|
||||
|
||||
func (a AuthLevel) CanDelete() bool {
|
||||
return a >= AuthDelete
|
||||
}
|
||||
|
||||
func (a AuthLevel) CanDoAny() bool {
|
||||
return a >= AuthDelete
|
||||
}
|
||||
|
||||
// 资源权限
|
||||
|
||||
type Auth struct {
|
||||
BaseModel
|
||||
Name string `json:"name"`
|
||||
AppID uint `json:"app_id"`
|
||||
// 权限标签
|
||||
Tag string `json:"tag"`
|
||||
// 权限等级 0 相当于没有 1 读权限 2 创建权限 3 修改权限 4 删除权限
|
||||
Level AuthLevel `json:"level"`
|
||||
}
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/veypi/utils"
|
||||
"github.com/veypi/utils/log"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// User db user model
|
||||
type User struct {
|
||||
BaseModel
|
||||
Username string `json:"username" gorm:"type:varchar(100);unique;not null"`
|
||||
Nickname string `json:"nickname" gorm:"type:varchar(100)" json:",omitempty"`
|
||||
Phone string `json:"phone" gorm:"type:varchar(20);unique;default:null" json:",omitempty"`
|
||||
Email string `json:"email" gorm:"type:varchar(50);unique;default:null" json:",omitempty"`
|
||||
CheckCode string `gorm:"type:varchar(64);not null" json:"-"`
|
||||
RealCode string `gorm:"type:varchar(32);not null" json:"-"`
|
||||
Position string `json:"position"`
|
||||
// disabled 禁用
|
||||
Status string `json:"status"`
|
||||
|
||||
Icon string `json:"icon"`
|
||||
Roles []*Role `json:"roles" gorm:"many2many:user_role;"`
|
||||
}
|
||||
|
||||
// TODO:: roles 是否会造成token过大 ?
|
||||
type PayLoad struct {
|
||||
ID uint `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Nickname string `json:"nickname"`
|
||||
Icon string `json:"icon"`
|
||||
Iat int64 `json:"iat"` //token time
|
||||
Exp int64 `json:"exp"`
|
||||
Roles []uint `json:"roles"`
|
||||
}
|
||||
|
||||
func (p *PayLoad) CheckAuth(name string, tags ...string) AuthLevel {
|
||||
res := AuthNone
|
||||
if p == nil || p.Roles == nil {
|
||||
return res
|
||||
}
|
||||
for _, id := range p.Roles {
|
||||
r := GlobalRoles[id]
|
||||
if r == nil {
|
||||
log.Warn().Msgf("not found role id: %d", id)
|
||||
continue
|
||||
}
|
||||
t := r.CheckAuth(name, tags...)
|
||||
if t > res {
|
||||
res = t
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (u *User) String() string {
|
||||
return u.Username + ":" + u.Nickname
|
||||
}
|
||||
|
||||
func (u *User) CheckAuth(name string, tags ...string) AuthLevel {
|
||||
res := AuthNone
|
||||
if u == nil || u.Roles == nil {
|
||||
return res
|
||||
}
|
||||
for _, t := range u.Roles {
|
||||
r := GlobalRoles[t.ID]
|
||||
if r == nil {
|
||||
log.Warn().Msgf("not found role id: %d", t.ID)
|
||||
continue
|
||||
}
|
||||
t := r.CheckAuth(name, tags...)
|
||||
if t > res {
|
||||
res = t
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (u *User) GetToken(key string) (string, error) {
|
||||
header := map[string]string{
|
||||
"typ": "JWT",
|
||||
"alg": "HS256",
|
||||
}
|
||||
//header := "{\"typ\": \"JWT\", \"alg\": \"HS256\"}"
|
||||
now := time.Now().Unix()
|
||||
payload := PayLoad{
|
||||
ID: u.ID,
|
||||
Username: u.Username,
|
||||
Nickname: u.Nickname,
|
||||
Icon: u.Icon,
|
||||
Iat: now,
|
||||
Exp: now + 60*60*24,
|
||||
}
|
||||
for _, r := range u.Roles {
|
||||
payload.Roles = append(payload.Roles, r.ID)
|
||||
}
|
||||
a, err := json.Marshal(header)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
b, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
A := base64.StdEncoding.EncodeToString(a)
|
||||
B := base64.StdEncoding.EncodeToString(b)
|
||||
hmacCipher := hmac.New(sha256.New, []byte(key))
|
||||
hmacCipher.Write([]byte(A + "." + B))
|
||||
C := hmacCipher.Sum(nil)
|
||||
return A + "." + B + "." + base64.StdEncoding.EncodeToString(C), nil
|
||||
}
|
||||
|
||||
var (
|
||||
InvalidToken = errors.New("invalid token")
|
||||
ExpiredToken = errors.New("expired token")
|
||||
)
|
||||
|
||||
func ParseToken(token string, key string, payload *PayLoad) (bool, error) {
|
||||
var A, B, C string
|
||||
if seqs := strings.Split(token, "."); len(seqs) == 3 {
|
||||
A, B, C = seqs[0], seqs[1], seqs[2]
|
||||
} else {
|
||||
return false, InvalidToken
|
||||
}
|
||||
hmacCipher := hmac.New(sha256.New, []byte(key))
|
||||
hmacCipher.Write([]byte(A + "." + B))
|
||||
tempC := hmacCipher.Sum(nil)
|
||||
if !hmac.Equal([]byte(C), []byte(base64.StdEncoding.EncodeToString(tempC))) {
|
||||
return false, nil
|
||||
}
|
||||
tempPayload, err := base64.StdEncoding.DecodeString(B)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if err := json.Unmarshal(tempPayload, payload); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if time.Now().Unix() > payload.Exp {
|
||||
return false, ExpiredToken
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (u *User) UpdateAuth(ps string) (err error) {
|
||||
u.RealCode = utils.RandSeq(32)
|
||||
u.CheckCode, err = utils.AesEncrypt(u.RealCode, []byte(ps))
|
||||
return err
|
||||
}
|
||||
|
||||
func (u *User) CheckLogin(ps string) (bool, error) {
|
||||
temp, err := utils.AesDecrypt(u.CheckCode, []byte(ps))
|
||||
return temp == u.RealCode, err
|
||||
}
|
||||
Reference in New Issue
Block a user