用户加密机制设计初步完成

master
veypi 3 years ago
parent cd7029c298
commit 82b64a4bb2

@ -2,6 +2,7 @@ package api
import ( import (
"OneAuth/api/app" "OneAuth/api/app"
"OneAuth/api/role"
"OneAuth/api/user" "OneAuth/api/user"
"OneAuth/api/wx" "OneAuth/api/wx"
"github.com/veypi/OneBD" "github.com/veypi/OneBD"
@ -18,5 +19,7 @@ func Router(r OneBD.Router) {
user.Router(r.SubRouter("/user")) user.Router(r.SubRouter("/user"))
wx.Router(r.SubRouter("wx")) wx.Router(r.SubRouter("wx"))
app.Router(r.SubRouter("app")) app.Router(r.SubRouter("app"))
role.Router(r)
//message.Router(r.SubRouter("/message")) //message.Router(r.SubRouter("/message"))
} }

@ -2,6 +2,7 @@ package app
import ( import (
"OneAuth/cfg" "OneAuth/cfg"
"OneAuth/libs/auth"
"OneAuth/libs/base" "OneAuth/libs/base"
"OneAuth/libs/oerr" "OneAuth/libs/oerr"
"OneAuth/models" "OneAuth/models"
@ -26,14 +27,40 @@ type appHandler struct {
func (h *appHandler) Get() (interface{}, error) { func (h *appHandler) Get() (interface{}, error) {
id := h.Meta().Params("id") id := h.Meta().Params("id")
if id == "" {
return nil, oerr.ApiArgsMissing
}
h.query = &models.App{} h.query = &models.App{}
h.query.UUID = id isSelf := h.Meta().Query("is_self")
err := cfg.DB().Where(h.query).First(h.query).Error if isSelf != "" {
// 无权限可以获取本系统基本信息
h.query.ID = cfg.CFG.APPID
err := cfg.DB().Where(h.query).First(h.query).Error
return h.query, err
}
err := h.ParsePayload(h.Meta())
if err != nil { if err != nil {
return nil, err return nil, err
} }
return h.query, nil if !h.GetAuth(auth.APP, id).CanRead() {
return nil, oerr.NoAuth
}
if id != "" {
h.query.UUID = id
err := cfg.DB().Where(h.query).First(h.query).Error
return h.query, err
}
// 注释代码为获取已经绑定的应用
//user := &models.User{}
//user.ID = h.Payload.ID
//err := cfg.DB().Preload("Roles.Auths").Preload("Auths").Where(user).First(user).Error
//if err != nil {
// return nil, oerr.DBErr.Attach(err)
//}
//ids := make([]string, 0, 10)
//for _, a := range user.GetAuths() {
// if a.RID == auth.Login && a.Level.CanDo() {
// ids = append(ids, a.RUID)
// }
//}
list := make([]*models.App, 0, 10)
err = cfg.DB().Find(&list).Error
return list, err
} }

@ -2,7 +2,9 @@ package role
import ( import (
"OneAuth/cfg" "OneAuth/cfg"
"OneAuth/libs/auth"
"OneAuth/libs/base" "OneAuth/libs/base"
"OneAuth/libs/oerr"
"OneAuth/models" "OneAuth/models"
"github.com/veypi/OneBD" "github.com/veypi/OneBD"
"github.com/veypi/OneBD/core" "github.com/veypi/OneBD/core"
@ -17,6 +19,9 @@ type authHandler struct {
} }
func (h *authHandler) Get() (interface{}, error) { func (h *authHandler) Get() (interface{}, error) {
if !h.GetAuth(auth.Auth).CanRead() {
return nil, oerr.NoAuth
}
l := make([]*models.Auth, 0, 10) l := make([]*models.Auth, 0, 10)
return &l, cfg.DB().Find(&l).Error return &l, cfg.DB().Find(&l).Error
} }

@ -2,6 +2,7 @@ package role
import ( import (
"OneAuth/cfg" "OneAuth/cfg"
"OneAuth/libs/auth"
"OneAuth/libs/base" "OneAuth/libs/base"
"OneAuth/libs/oerr" "OneAuth/libs/oerr"
"OneAuth/models" "OneAuth/models"
@ -20,8 +21,9 @@ type roleHandler struct {
func (h *roleHandler) Get() (interface{}, error) { func (h *roleHandler) Get() (interface{}, error) {
id := h.Meta().ParamsInt("id") id := h.Meta().ParamsInt("id")
aid := h.Meta().ParamsInt("aid") if !h.GetAuth(auth.Role, h.Meta().Params("id")).CanRead() {
act := h.Meta().Params("action") return nil, oerr.NoAuth
}
if id > 0 { if id > 0 {
role := &models.Role{} role := &models.Role{}
role.ID = uint(id) role.ID = uint(id)
@ -29,21 +31,6 @@ func (h *roleHandler) Get() (interface{}, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if aid <= 0 {
return role, nil
}
if !h.CheckAuth("admin", "").CanDoAny() {
return nil, oerr.NoAuth
}
at := &models.RoleAuth{}
at.RoleID = role.ID
at.AuthID = uint(aid)
defer models.SyncGlobalRoles()
if act == "bind" {
err = cfg.DB().Where(at).FirstOrCreate(at).Error
} else if act == "unbind" {
err = cfg.DB().Where(at).Delete(at).Error
}
return role, nil return role, nil
} }
roles := make([]*models.Role, 0, 10) roles := make([]*models.Role, 0, 10)
@ -52,7 +39,7 @@ func (h *roleHandler) Get() (interface{}, error) {
} }
func (h *roleHandler) Post() (interface{}, error) { func (h *roleHandler) Post() (interface{}, error) {
if !h.CheckAuth("role", "").CanCreate() { if !h.GetAuth(auth.Role).CanCreate() {
return nil, oerr.NoAuth return nil, oerr.NoAuth
} }
role := &models.Role{} role := &models.Role{}
@ -61,15 +48,14 @@ func (h *roleHandler) Post() (interface{}, error) {
return nil, err return nil, err
} }
role.ID = 0 role.ID = 0
if role.Category == 0 || role.Name == "" { if role.Name == "" {
return nil, oerr.ApiArgsMissing return nil, oerr.ApiArgsMissing
} }
defer models.SyncGlobalRoles()
return role, cfg.DB().Where(role).FirstOrCreate(role).Error return role, cfg.DB().Where(role).FirstOrCreate(role).Error
} }
func (h *roleHandler) Patch() (interface{}, error) { func (h *roleHandler) Patch() (interface{}, error) {
if !h.CheckAuth("role", "").CanUpdate() { if !h.GetAuth(auth.Role).CanUpdate() {
return nil, oerr.NoAuth return nil, oerr.NoAuth
} }
query := &struct { query := &struct {
@ -120,7 +106,7 @@ func (h *roleHandler) Patch() (interface{}, error) {
} }
func (h *roleHandler) Delete() (interface{}, error) { func (h *roleHandler) Delete() (interface{}, error) {
if !h.CheckAuth("role").CanDelete() { if !h.GetAuth(auth.Role).CanDelete() {
return nil, oerr.NoAuth return nil, oerr.NoAuth
} }
rid := h.Meta().ParamsInt("id") rid := h.Meta().ParamsInt("id")
@ -133,6 +119,5 @@ func (h *roleHandler) Delete() (interface{}, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer models.SyncGlobalRoles()
return nil, cfg.DB().Delete(role).Error return nil, cfg.DB().Delete(role).Error
} }

@ -8,6 +8,6 @@ import (
func Router(r OneBD.Router) { func Router(r OneBD.Router) {
r.Set("/role/", roleP, rfc.MethodGet, rfc.MethodPost) r.Set("/role/", roleP, rfc.MethodGet, rfc.MethodPost)
r.Set("/role/:id", roleP, rfc.MethodGet, rfc.MethodDelete, rfc.MethodPatch) r.Set("/role/:id", roleP, rfc.MethodGet, rfc.MethodDelete, rfc.MethodPatch)
r.Set("/role/:id/:action/:aid", roleP, rfc.MethodGet) r.Set("/role/:id/:action/:rid", roleP, rfc.MethodGet)
r.Set("/auth/", authP, rfc.MethodGet) r.Set("/auth/", authP, rfc.MethodGet)
} }

@ -2,9 +2,13 @@ package user
import ( import (
"OneAuth/cfg" "OneAuth/cfg"
"OneAuth/libs/app"
"OneAuth/libs/base" "OneAuth/libs/base"
"OneAuth/libs/oerr" "OneAuth/libs/oerr"
"OneAuth/libs/token"
"OneAuth/models" "OneAuth/models"
"errors"
//"OneAuth/ws" //"OneAuth/ws"
"encoding/base64" "encoding/base64"
"fmt" "fmt"
@ -68,10 +72,17 @@ func (h *handler) Get() (interface{}, error) {
// Post register user // Post register user
func (h *handler) Post() (interface{}, error) { func (h *handler) Post() (interface{}, error) {
if !cfg.CFG.EnableRegister { self := &models.App{}
return nil, oerr.NoAuth.AttachStr("register disabled.") self.ID = cfg.CFG.APPID
err := cfg.DB().Where(self).First(self).Error
if err != nil {
return nil, oerr.DBErr.Attach(err)
}
if !self.EnableRegister {
return nil, oerr.NoAuth.AttachStr("register disabled")
} }
var userdata = struct { var userdata = struct {
UUID string `json:"uuid"`
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`
Nickname string `json:"nickname"` Nickname string `json:"nickname"`
@ -101,16 +112,40 @@ func (h *handler) Post() (interface{}, error) {
h.User.Username = userdata.Username h.User.Username = userdata.Username
h.User.Email = userdata.Email h.User.Email = userdata.Email
h.User.Position = userdata.Position h.User.Position = userdata.Position
if err := h.User.UpdateAuth(string(pass)); err != nil { if err := h.User.UpdatePass(string(pass)); err != nil {
log.HandlerErrs(err) log.HandlerErrs(err)
return nil, oerr.ResourceCreatedFailed return nil, oerr.ResourceCreatedFailed
} }
tx := cfg.DB().Begin() err = cfg.DB().Transaction(func(tx *gorm.DB) error {
if err := tx.Create(&h.User).Error; err != nil { if err := tx.Create(&h.User).Error; err != nil {
tx.Rollback() return oerr.ResourceDuplicated
return nil, oerr.ResourceDuplicated }
err := app.AddUser(tx, self.ID, h.User.ID, self.InitRoleID)
if err != nil {
return err
}
if userdata.UUID != self.UUID && userdata.UUID != "" {
target := &models.App{}
target.UUID = userdata.UUID
err = tx.Where(target).First(target).Error
if err != nil {
return err
}
if target.EnableRegister {
err := app.AddUser(tx, target.ID, h.User.ID, target.InitRoleID)
if err != nil {
return err
}
} else {
// TODO
}
return err
}
return nil
})
if err != nil {
return nil, err
} }
tx.Commit()
return h.User, nil return h.User, nil
} }
@ -142,7 +177,7 @@ func (h *handler) Patch() (interface{}, error) {
return nil, oerr.NoAuth return nil, oerr.NoAuth
} }
if len(opts.Password) >= 6 { if len(opts.Password) >= 6 {
if err := target.UpdateAuth(opts.Password); err != nil { if err := target.UpdatePass(opts.Password); err != nil {
log.HandlerErrs(err) log.HandlerErrs(err)
return nil, oerr.ApiArgsError.AttachStr(err.Error()) return nil, oerr.ApiArgsError.AttachStr(err.Error())
} }
@ -187,9 +222,9 @@ func (h *handler) Head() (interface{}, error) {
if len(uid) == 0 || len(password) == 0 { if len(uid) == 0 || len(password) == 0 {
return nil, oerr.ApiArgsError return nil, oerr.ApiArgsError
} }
appID, err := strconv.Atoi(h.Meta().Query("app_id")) appUUID := h.Meta().Query("uuid")
if err != nil || appID <= 0 { if appUUID == "" {
return nil, oerr.ApiArgsMissing return nil, oerr.ApiArgsMissing.AttachStr("uuid")
} }
h.User = new(models.User) h.User = new(models.User)
uidType := h.Meta().Query("uid_type") uidType := h.Meta().Query("uid_type")
@ -203,32 +238,15 @@ func (h *handler) Head() (interface{}, error) {
default: default:
h.User.Username = uid h.User.Username = uid
} }
app := &models.App{} target := &models.App{}
app.ID = uint(appID) target.UUID = appUUID
err = cfg.DB().Where(app).Find(app).Error err = cfg.DB().Where(target).Find(target).Error
if err != nil { if err != nil {
return nil, oerr.DBErr.Attach(err) return nil, oerr.DBErr.Attach(err)
} }
if err := cfg.DB().Preload("Roles").Where(h.User).First(h.User).Error; err != nil { if err := cfg.DB().Preload("Roles.Auths").Preload("Auths").Where(h.User).First(h.User).Error; err != nil {
if err.Error() == gorm.ErrRecordNotFound.Error() { if err.Error() == gorm.ErrRecordNotFound.Error() {
// admin 登录自动注册 return nil, oerr.AccountNotExist
if h.User.Username == "admin" {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
h.User.Icon = fmt.Sprintf("/media/icon/default/%04d.jpg", r.Intn(230))
err = h.User.UpdateAuth(password)
if err != nil {
return nil, err
}
role := &models.Role{}
role.ID = 1
h.User.Roles = []*models.Role{role}
err = cfg.DB().Create(h.User).Error
if err != nil {
return nil, err
}
} else {
return nil, oerr.AccountNotExist
}
} else { } else {
log.HandlerErrs(err) log.HandlerErrs(err)
return nil, oerr.DBErr.Attach(err) return nil, oerr.DBErr.Attach(err)
@ -238,15 +256,27 @@ func (h *handler) Head() (interface{}, error) {
if err != nil || !isAuth { if err != nil || !isAuth {
return nil, oerr.PassError.Attach(err) return nil, oerr.PassError.Attach(err)
} }
if h.User.Status == "disabled" { au := &models.AppUser{}
au.UserID = h.User.ID
au.AppID = target.ID
err = cfg.DB().Where(au).First(au).Error
appID := target.ID
h.Meta().SetHeader("content", target.UserRefreshUrl)
if err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return nil, err
}
appID = cfg.CFG.APPID
h.Meta().SetHeader("content", "/app/"+target.UUID)
} else if au.Disabled {
return nil, oerr.DisableLogin return nil, oerr.DisableLogin
} }
token, err := h.User.GetToken(app.Key, app.ID) tokenStr, err := token.GetToken(h.User, appID)
if err != nil { if err != nil {
log.HandlerErrs(err) log.HandlerErrs(err)
return nil, oerr.Unknown.Attach(err) return nil, oerr.Unknown.Attach(err)
} }
h.Meta().SetHeader("auth_token", token) h.Meta().SetHeader("auth_token", tokenStr)
log.Info().Msg(h.User.Username + " login") log.Info().Msg(h.User.Username + " login")
return nil, nil return nil, nil
} }

@ -35,9 +35,8 @@ func (h *userRoleHandler) Post() (interface{}, error) {
err = cfg.DB().First(query, query.ID).Error err = cfg.DB().First(query, query.ID).Error
} else if query.Name != "" { } else if query.Name != "" {
err = cfg.DB().Where(map[string]interface{}{ err = cfg.DB().Where(map[string]interface{}{
"name": query.Name, "name": query.Name,
"category": query.Category, "tag": query.Tag,
"tag": query.Tag,
}).First(query).Error }).First(query).Error
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
err = cfg.DB().Create(query).Error err = cfg.DB().Create(query).Error

@ -8,19 +8,19 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
) )
var Path = cmd.GetCfgPath("OneAuth", "oa") var Path = cmd.GetCfgPath("oa", "settings")
var CFG = &struct { var CFG = &struct {
AdminUser string AdminUser string
Host string Host string
LoggerPath string LoggerPath string
LoggerLevel string LoggerLevel string
Key string APPID uint
TimeFormat string APPKey string
Debug bool TimeFormat string
EXEDir string Debug bool
EnableRegister bool EXEDir string
DB struct { DB struct {
Type string Type string
Addr string Addr string
User string User string
@ -28,14 +28,14 @@ var CFG = &struct {
DB string DB string
} }
}{ }{
AdminUser: "admin", APPID: 1,
Host: "0.0.0.0:4001", AdminUser: "admin",
LoggerPath: "", Host: "0.0.0.0:4001",
LoggerLevel: "debug", LoggerPath: "",
TimeFormat: "2006/01/02 15:04:05", LoggerLevel: "debug",
Debug: true, TimeFormat: "2006/01/02 15:04:05",
EXEDir: "./", Debug: true,
EnableRegister: true, EXEDir: "./",
DB: struct { DB: struct {
Type string Type string
Addr string Addr string

@ -0,0 +1,48 @@
package app
import (
"OneAuth/libs/auth"
"OneAuth/libs/oerr"
"OneAuth/models"
"errors"
"gorm.io/gorm"
)
func AddUser(tx *gorm.DB, appID uint, userID uint, roleID uint) error {
au := &models.AppUser{}
au.AppID = appID
au.UserID = userID
err := tx.Where(au).First(au).Error
if err == nil {
return oerr.ResourceDuplicated
}
if errors.Is(err, gorm.ErrRecordNotFound) {
err = tx.Create(au).Error
if err != nil {
return err
}
err = auth.BindUserRole(tx, userID, roleID)
if err != nil {
return err
}
return tx.Model(&models.App{}).Where("id = ?", appID).Update("user_count", gorm.Expr("user_count + ?", 1)).Error
}
return err
}
func EnableUser(tx *gorm.DB, appID uint, userID uint) error {
au := &models.AppUser{}
au.AppID = appID
au.UserID = userID
err := tx.Where(au).First(au).Error
if err != nil {
return err
}
return tx.Where(au).Update("disabled", false).Error
}
func DisableUser(tx *gorm.DB, appID uint, userID uint) error {
au := &models.AppUser{}
au.AppID = appID
au.UserID = userID
return tx.Where(au).Update("disabled", true).Error
}

@ -0,0 +1,66 @@
package auth
import (
"OneAuth/models"
"gorm.io/gorm"
)
// 定义oa系统权限
type Resource = string
const (
User Resource = "user"
APP Resource = "app"
Res Resource = "resource"
Role Resource = "role"
Auth Resource = "auth"
)
func BindUserRole(tx *gorm.DB, userID uint, roleID uint) error {
r := &models.Role{}
r.ID = roleID
err := tx.Where(r).First(r).Error
if err != nil {
return err
}
ur := &models.UserRole{}
ur.RoleID = roleID
if r.IsUnique {
err = tx.Where(ur).Update("user_id", userID).Error
} else {
ur.UserID = userID
err = tx.Where(ur).FirstOrCreate(ur).Error
}
return err
}
func BindUserAuth(tx *gorm.DB, userID uint, resID uint, level models.AuthLevel, ruid string) error {
return bind(tx, userID, resID, level, ruid, false)
}
func BindRoleAuth(tx *gorm.DB, roleID uint, resID uint, level models.AuthLevel, ruid string) error {
return bind(tx, roleID, resID, level, ruid, true)
}
func bind(tx *gorm.DB, id uint, resID uint, level models.AuthLevel, ruid string, isRole bool) error {
r := &models.Resource{}
r.ID = resID
err := tx.Where(r).First(r).Error
if err != nil {
return err
}
au := &models.Auth{
AppID: r.AppID,
ResourceID: resID,
RID: r.Name,
RUID: ruid,
Level: level,
}
if isRole {
au.RoleID = &id
} else {
au.UserID = &id
}
return tx.Where(au).FirstOrCreate(au).Error
}

@ -1,7 +1,6 @@
package base package base
import ( import (
"OneAuth/libs/auth"
"OneAuth/libs/oerr" "OneAuth/libs/oerr"
"OneAuth/libs/tools" "OneAuth/libs/tools"
"github.com/json-iterator/go" "github.com/json-iterator/go"
@ -17,7 +16,7 @@ var json = jsoniter.ConfigFastest
type ApiHandler struct { type ApiHandler struct {
OneBD.BaseHandler OneBD.BaseHandler
auth.UserHandler UserHandler
} }
func (h *ApiHandler) Init(m OneBD.Meta) error { func (h *ApiHandler) Init(m OneBD.Meta) error {

@ -1,15 +1,15 @@
package auth package base
import ( import (
"OneAuth/cfg"
"OneAuth/libs/oerr" "OneAuth/libs/oerr"
"OneAuth/libs/token"
"OneAuth/models" "OneAuth/models"
"github.com/veypi/OneBD" "github.com/veypi/OneBD"
"github.com/veypi/OneBD/rfc" "github.com/veypi/OneBD/rfc"
) )
type UserHandler struct { type UserHandler struct {
Payload *models.PayLoad Payload *token.PayLoad
ignoreMethod map[rfc.Method]bool ignoreMethod map[rfc.Method]bool
} }
@ -17,12 +17,16 @@ func (a *UserHandler) Init(m OneBD.Meta) error {
if a.ignoreMethod != nil && a.ignoreMethod[m.Method()] { if a.ignoreMethod != nil && a.ignoreMethod[m.Method()] {
return nil return nil
} }
a.Payload = new(models.PayLoad) return a.ParsePayload(m)
token := m.GetHeader("auth_token") }
if token == "" {
func (a *UserHandler) ParsePayload(m OneBD.Meta) error {
a.Payload = new(token.PayLoad)
tokenStr := m.GetHeader("auth_token")
if tokenStr == "" {
return oerr.NotLogin return oerr.NotLogin
} }
ok, err := models.ParseToken(token, cfg.CFG.Key, a.Payload) ok, err := token.ParseToken(tokenStr, a.Payload)
if ok { if ok {
return nil return nil
} }

@ -0,0 +1,11 @@
package key
import "OneAuth/cfg"
func App(id uint) string {
if id == cfg.CFG.APPID {
return cfg.CFG.APPKey
}
// TODO
return ""
}

@ -1,24 +1,24 @@
package auth package key
import ( import (
"OneAuth/models" "OneAuth/cfg"
"github.com/veypi/utils" "github.com/veypi/utils"
"sync" "sync"
) )
var keyCache = sync.Map{} var keyCache = sync.Map{}
func GetUserKey(uid uint, app *models.App) string { func User(uid uint, appID uint) string {
if app.ID == 1 { if appID == cfg.CFG.APPID {
key, _ := keyCache.LoadOrStore(uid, utils.RandSeq(16)) key, _ := keyCache.LoadOrStore(uid, utils.RandSeq(16))
return key.(string) return cfg.CFG.APPKey + key.(string)
} }
// TODO: 获取其他应用user_key // TODO: 获取其他应用user_key
return "" return ""
} }
func RefreshUserKey(uid uint, app *models.App) string { func RefreshUser(uid uint, appID uint) string {
if app.ID == 1 { if appID == cfg.CFG.APPID {
key := utils.RandSeq(16) key := utils.RandSeq(16)
keyCache.Store(uid, key) keyCache.Store(uid, key)
return key return key

@ -0,0 +1,127 @@
package token
import (
"OneAuth/libs/key"
"OneAuth/models"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"errors"
"strings"
"time"
)
var (
InvalidToken = errors.New("invalid token")
ExpiredToken = errors.New("expired token")
)
type simpleAuth struct {
RID string `json:"rid"`
// 具体某个资源的id
RUID string `json:"ruid"`
Level models.AuthLevel `json:"level"`
}
// TODO:: roles 是否会造成token过大 ?
type PayLoad struct {
ID uint `json:"id"`
AppID uint `json:"app_id"`
Iat int64 `json:"iat"` //token time
Exp int64 `json:"exp"`
Auth map[uint]*simpleAuth `json:"auth"`
}
// GetAuth resource_uuid 缺省或仅第一个有效 权限会被更高权限覆盖
func (p *PayLoad) GetAuth(ResourceID string, ResourceUUID ...string) models.AuthLevel {
res := models.AuthNone
if p == nil || p.Auth == nil {
return res
}
ruid := ""
if len(ResourceUUID) > 0 {
ruid = ResourceUUID[0]
}
for _, a := range p.Auth {
if a.RID == ResourceID {
if a.RUID != "" {
if a.RUID == ruid {
if a.Level > res {
res = a.Level
}
} else {
continue
}
} else if a.Level > res {
res = a.Level
}
}
}
return res
}
func GetToken(u *models.User, appID uint) (string, error) {
header := map[string]string{
"typ": "JWT",
"alg": "HS256",
}
//header := "{\"typ\": \"JWT\", \"alg\": \"HS256\"}"
now := time.Now().Unix()
payload := PayLoad{
ID: u.ID,
AppID: appID,
Iat: now,
Exp: now + 60*60*24,
Auth: map[uint]*simpleAuth{},
}
for _, a := range u.GetAuths() {
if appID == a.AppID {
payload.Auth[a.ID] = &simpleAuth{
RID: a.RID,
RUID: a.RUID,
Level: a.Level,
}
}
}
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.User(payload.ID, payload.AppID)))
hmacCipher.Write([]byte(A + "." + B))
C := hmacCipher.Sum(nil)
return A + "." + B + "." + base64.StdEncoding.EncodeToString(C), nil
}
func ParseToken(token 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
}
tempPayload, err := base64.StdEncoding.DecodeString(B)
if err != nil {
return false, err
}
if err := json.Unmarshal(tempPayload, payload); err != nil {
return false, err
}
hmacCipher := hmac.New(sha256.New, []byte(key.User(payload.ID, payload.AppID)))
hmacCipher.Write([]byte(A + "." + B))
tempC := hmacCipher.Sum(nil)
if !hmac.Equal([]byte(C), []byte(base64.StdEncoding.EncodeToString(tempC))) {
return false, nil
}
if time.Now().Unix() > payload.Exp {
return false, ExpiredToken
}
return true, nil
}

@ -35,10 +35,15 @@ func main() {
Value: cfg.CFG.LoggerPath, Value: cfg.CFG.LoggerPath,
Destination: &cfg.CFG.LoggerPath, Destination: &cfg.CFG.LoggerPath,
}, },
&cli.UintFlag{
Name: "id",
Value: cfg.CFG.APPID,
Destination: &cfg.CFG.APPID,
},
&cli.StringFlag{ &cli.StringFlag{
Name: "key", Name: "key",
Value: cfg.CFG.Key, Value: cfg.CFG.APPKey,
Destination: &cfg.CFG.Key, Destination: &cfg.CFG.APPKey,
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "exe_dir", Name: "exe_dir",
@ -52,7 +57,11 @@ func main() {
}, },
} }
app.Commands = []*cli.Command{ app.Commands = []*cli.Command{
&sub.Web, sub.Web,
sub.App,
sub.Role,
sub.Resource,
sub.Init,
} }
srv, err := cmd.NewSrv(app, sub.RunWeb, cfg.CFG, cfg.Path) srv, err := cmd.NewSrv(app, sub.RunWeb, cfg.CFG, cfg.Path)
if err != nil { if err != nil {

@ -4,9 +4,20 @@ var AppKeys = map[string]string{}
type App struct { type App struct {
BaseModel BaseModel
Name string `json:"name"` Name string `json:"name"`
Icon string `json:"icon"` Icon string `json:"icon"`
UUID string `json:"uuid"` UUID string `json:"uuid" gorm:"unique"`
Des string `json:"des"`
Creator uint `json:"creator"`
UserCount uint `json:"user_count"`
Users []*User `json:"users" gorm:"many2many:app_users;"`
// 初始用户角色
InitRoleID uint `json:"init_role_id"`
InitRole *Role `json:"init_role"`
// 是否在首页隐藏
Hide bool `json:"hide"`
// PubKey string `json:"pub_key"`
// PrivateKey string `json:"private_key"`
// 认证成功跳转链接 // 认证成功跳转链接
Host string `json:"host"` Host string `json:"host"`
// 加解密用户token (key+key2) // 加解密用户token (key+key2)
@ -14,11 +25,13 @@ type App struct {
// key oa发放给app 双方保存 针对app生成 每个应用有一个 // key oa发放给app 双方保存 针对app生成 每个应用有一个
// key2 app发放给oa app保存 oa使用一次销毁 针对当个用户生成 每个用户有一个 // key2 app发放给oa app保存 oa使用一次销毁 针对当个用户生成 每个用户有一个
// 获取app用户加密秘钥key2 // 获取app用户加密秘钥key2
// TODO
UserRefreshUrl string `json:"user_refresh_url"` UserRefreshUrl string `json:"user_refresh_url"`
// app 校验用户token时使用 // app 校验用户token时使用
Key string `json:"key"` Key string `json:"-"`
// 是否允许用户注册 // 是否允许用户自主注册
EnableRegister string `json:"enable_register"` EnableRegister bool `json:"enable_register"`
EnableUserKey bool `json:"enable_user_key"`
EnableUser bool `json:"enable_user"` EnableUser bool `json:"enable_user"`
EnableWx bool `json:"enable_wx"` EnableWx bool `json:"enable_wx"`
EnablePhone bool `json:"enable_phone"` EnablePhone bool `json:"enable_phone"`
@ -26,6 +39,14 @@ type App struct {
Wx *Wechat `json:"wx" gorm:"foreignkey:AppID;references:ID"` Wx *Wechat `json:"wx" gorm:"foreignkey:AppID;references:ID"`
} }
type AppUser struct {
BaseModel
AppID uint `json:"app_id"`
UserID uint `json:"user_id"`
Disabled bool `json:"disabled"`
Status string `json:"status"`
}
type Wechat struct { type Wechat struct {
BaseModel BaseModel
AppID uint `json:"app_id"` AppID uint `json:"app_id"`

@ -8,19 +8,22 @@ type UserRole struct {
type Role struct { type Role struct {
BaseModel BaseModel
Name string `json:"name"` AppID uint `json:"app_id"`
// 角色类型 App *App `json:"app"`
// 1: 系统定义角色 2: 用户自定义角色 Name string `json:"name"`
Category uint `json:"category" gorm:"default:1"`
// 角色标签 // 角色标签
Tag string `json:"tag" gorm:"default:''"` Tag string `json:"tag" gorm:"default:''"`
Users []*User `json:"users" gorm:"many2many:user_role;"` Users []*User `json:"users" gorm:"many2many:user_roles;"`
// 具体权限 // 具体权限
Auths []*Auth `json:"auths" gorm:"foreignkey:RoleID;references:ID"` Auths []*Auth `json:"auths" gorm:"foreignkey:RoleID;references:ID"`
IsUnique bool `json:"is_unique" gorm:"default:false"` IsUnique bool `json:"is_unique" gorm:"default:false"`
} }
// AuthLevel 权限等级 // AuthLevel 权限等级
// 对于操作类权限
// 0 禁止执行
// 1 允许执行
// 对于资源类权限
// 0 相当于没有 // 0 相当于没有
// 1 有限读权限 // 1 有限读权限
// 2 读权限 // 2 读权限
@ -32,6 +35,7 @@ type AuthLevel uint
const ( const (
AuthNone AuthLevel = 0 AuthNone AuthLevel = 0
AuthDo AuthLevel = 1
// AuthPart TODO: 临时权限 // AuthPart TODO: 临时权限
AuthPart AuthLevel = 1 AuthPart AuthLevel = 1
AuthRead AuthLevel = 2 AuthRead AuthLevel = 2
@ -41,6 +45,14 @@ const (
AuthAll AuthLevel = 6 AuthAll AuthLevel = 6
) )
func (a AuthLevel) Upper(b AuthLevel) bool {
return a > b
}
func (a AuthLevel) CanDo() bool {
return a > AuthNone
}
func (a AuthLevel) CanRead() bool { func (a AuthLevel) CanRead() bool {
return a >= AuthRead return a >= AuthRead
} }
@ -61,22 +73,33 @@ func (a AuthLevel) CanDoAny() bool {
return a >= AuthAll return a >= AuthAll
} }
// 资源权限 // Auth 资源权限
type Auth struct { type Auth struct {
BaseModel BaseModel
Name string `json:"name"`
// 该权限作用的应用 // 该权限作用的应用
AppID uint `json:"app_id"` AppID uint `json:"app_id"`
App *App `json:"app"`
// 权限绑定只能绑定一个 // 权限绑定只能绑定一个
RoleID uint `json:"role_id"` RoleID *uint `json:"role_id" gorm:""`
UserID uint `json:"user_id"` Role *Role `json:"role"`
UserID *uint `json:"user_id"`
User *User `json:"user"`
// 资源id // 资源id
ResourceID uint `json:"resource_id" gorm:"not null"`
Resource *Resource `json:"resource"`
// resource_name 用于其他系统方便区分权限的名字
RID string `json:"rid" gorm:""` RID string `json:"rid" gorm:""`
// 具体某个资源的id // 具体某个资源的id
RUID string `json:"ruid"` RUID string `json:"ruid"`
// 权限标签
Tag string `json:"tag"`
Level AuthLevel `json:"level"` Level AuthLevel `json:"level"`
Des string `json:"des"` }
type Resource struct {
BaseModel
AppID uint `json:"app_id"`
App *App `json:"app"`
Name string `json:"name"`
// 权限标签
Tag string `json:"tag"`
Des string `json:"des"`
} }

@ -1,14 +1,7 @@
package models package models
import ( import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"errors"
"github.com/veypi/utils" "github.com/veypi/utils"
"strings"
"time"
) )
// User db user model // User db user model
@ -25,46 +18,45 @@ type User struct {
Status string `json:"status"` Status string `json:"status"`
Icon string `json:"icon"` Icon string `json:"icon"`
Roles []*Role `json:"roles" gorm:"many2many:user_role;"` Roles []*Role `json:"roles" gorm:"many2many:user_roles;"`
Apps []*App `json:"apps" gorm:"many2many:app_users;"`
Auths []*Auth `json:"auths" gorm:"foreignkey:UserID;references:ID"` Auths []*Auth `json:"auths" gorm:"foreignkey:UserID;references:ID"`
} }
type simpleAuth struct { func (u *User) String() string {
RID string `json:"rid"` return u.Username + ":" + u.Nickname
// 具体某个资源的id
RUID string `json:"ruid"`
Level AuthLevel `json:"level"`
} }
// TODO:: roles 是否会造成token过大 ? func (u *User) GetAuths() []*Auth {
type PayLoad struct { list := make([]*Auth, 0, 10)
ID uint `json:"id"` for _, r := range u.Roles {
Iat int64 `json:"iat"` //token time for _, a := range r.Auths {
Exp int64 `json:"exp"` list = append(list, a)
Auth map[uint]*simpleAuth `json:"auth"` }
}
for _, a := range u.Auths {
list = append(list, a)
}
return list
} }
// GetAuth resource_uuid 缺省或仅第一个有效 权限会被更高权限覆盖 func (u *User) GetAuth(appID uint, ResourceID string, ResourceUUID ...string) AuthLevel {
func (p *PayLoad) GetAuth(ResourceID string, ResourceUUID ...string) AuthLevel { var res = AuthNone
res := AuthNone
if p == nil || p.Auth == nil {
return res
}
ruid := "" ruid := ""
if len(ResourceUUID) > 0 { if len(ResourceUUID) > 0 {
ruid = ResourceUUID[0] ruid = ResourceUUID[0]
} }
for _, a := range p.Auth { for _, a := range u.GetAuths() {
if a.RID == ResourceID { if a.RID == ResourceID && a.AppID == appID {
if a.RUID != "" { if a.RUID != "" {
if a.RUID == ruid { if a.RUID == ruid {
if a.Level > res { if a.Level.Upper(res) {
res = a.Level res = a.Level
} }
} else { } else {
continue continue
} }
} else if a.Level > res { } else if a.Level.Upper(res) {
res = a.Level res = a.Level
} }
} }
@ -72,91 +64,7 @@ func (p *PayLoad) GetAuth(ResourceID string, ResourceUUID ...string) AuthLevel {
return res return res
} }
func (u *User) String() string { func (u *User) UpdatePass(ps string) (err error) {
return u.Username + ":" + u.Nickname
}
func (u *User) GetToken(key string, appID uint) (string, error) {
header := map[string]string{
"typ": "JWT",
"alg": "HS256",
}
//header := "{\"typ\": \"JWT\", \"alg\": \"HS256\"}"
now := time.Now().Unix()
payload := PayLoad{
ID: u.ID,
Iat: now,
Exp: now + 60*60*24,
Auth: map[uint]*simpleAuth{},
}
for _, r := range u.Roles {
for _, a := range r.Auths {
if appID == a.AppID {
payload.Auth[a.ID] = &simpleAuth{
RID: a.RID,
RUID: a.RUID,
Level: a.Level,
}
}
}
}
for _, a := range u.Auths {
if appID == a.AppID {
payload.Auth[a.ID] = &simpleAuth{
RID: a.RID,
RUID: a.RUID,
Level: a.Level,
}
}
}
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.RealCode = utils.RandSeq(32)
u.CheckCode, err = utils.AesEncrypt(u.RealCode, []byte(ps)) u.CheckCode, err = utils.AesEncrypt(u.RealCode, []byte(ps))
return err return err

@ -8,11 +8,13 @@
"lint": "vue-cli-service lint" "lint": "vue-cli-service lint"
}, },
"dependencies": { "dependencies": {
"@veypi/one-icon": "^1.0.1",
"axios": "^0.21.1", "axios": "^0.21.1",
"core-js": "^3.6.5", "core-js": "^3.6.5",
"js-base64": "^3.6.0", "js-base64": "^3.6.0",
"vue": "^2.6.11", "vue": "^2.6.11",
"vue-class-component": "^7.2.3", "vue-class-component": "^7.2.3",
"vue-m-message": "^3.1.0",
"vue-property-decorator": "^9.1.2", "vue-property-decorator": "^9.1.2",
"vue-router": "^3.2.0", "vue-router": "^3.2.0",
"vuetify": "^2.4.0", "vuetify": "^2.4.0",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

@ -1,19 +1,20 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0"> <meta name="viewport" content="width=device-width,initial-scale=1.0">
<!-- <link rel="icon" href="<%= BASE_URL %>favicon.ico">--> <!-- <link rel="icon" href="<%= BASE_URL %>favicon.ico">-->
<title><%= htmlWebpackPlugin.options.title %></title> <title><%= htmlWebpackPlugin.options.title %></title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900"> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css">
</head> </head>
<body> <body>
<noscript> <noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
</noscript> Please enable it to continue.</strong>
<div id="app"></div> </noscript>
<!-- built files will be auto injected --> <div id="app"></div>
</body> <!-- built files will be auto injected -->
</body>
</html> </html>

@ -6,11 +6,10 @@
dark dark
> >
<div class="d-flex align-center"> <div class="d-flex align-center">
<one-icon style="color: aqua;font-size: 56px">glassdoor</one-icon>
<span class="font-italic font-weight-bold" style="font-size: 20px">统一认证</span>
</div> </div>
<v-spacer></v-spacer> <v-spacer></v-spacer>
<v-btn text class="font-italic" style="font-size: 20px">
统一认证
</v-btn>
</v-app-bar> </v-app-bar>
<v-main> <v-main>
@ -32,8 +31,9 @@ export default Vue.extend({
// //
}), }),
mounted() { beforeCreate() {
util.title('统一认证') util.title('统一认证')
this.$store.dispatch('fetchSelf')
} }
}) })
</script> </script>

@ -32,7 +32,7 @@ class Interface {
const newFail = function (data: any) { const newFail = function (data: any) {
if (data && data.code === 40001) { if (data && data.code === 40001) {
// no login // no login
store.dispatch('handleLogOut') store.dispatch('handleLogout')
return return
} }
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore // eslint-disable-next-line @typescript-eslint/ban-ts-ignore
@ -52,7 +52,7 @@ class Interface {
} else { } else {
newFail(data) newFail(data)
if (data.code === 41001) { if (data.code === 41001) {
store.dispatch('handleLogOut') store.dispatch('handleLogout')
// bus.$emit('log_out') // bus.$emit('log_out')
} }
} }
@ -106,6 +106,9 @@ const role = {
const app = { const app = {
local: '/api/app/', local: '/api/app/',
self() {
return new Interface(ajax.get, this.local, {is_self: true})
},
get(id: string) { get(id: string) {
return new Interface(ajax.get, this.local + id) return new Interface(ajax.get, this.local + id)
}, },
@ -116,15 +119,18 @@ const app = {
const user = { const user = {
local: '/api/user/', local: '/api/user/',
register(username: string, password: string, prop?: any) { register(username: string, password: string, uuid: string, prop?: any) {
const data = Object.assign({ const data = Object.assign({
username: username, username: username,
uuid: uuid,
password: Base64.encode(password) password: Base64.encode(password)
}, prop) }, prop)
return new Interface(ajax.post, this.local, data) return new Interface(ajax.post, this.local, data)
}, },
login(username: string, password: string) { login(username: string, password: string, uuid: string) {
return new Interface(ajax.head, this.local + username, { return new Interface(ajax.head, this.local + username, {
uid_type: 'username',
uuid: uuid,
password: Base64.encode(password) password: Base64.encode(password)
}) })
} }
@ -196,21 +202,6 @@ const api = {
return new Interface(ajax.get, '/api/user/', { return new Interface(ajax.get, '/api/user/', {
username username
}) })
},
login(username: string, password: string) {
return new Interface(ajax.head, '/api/user/' + username, {
password: Base64.encode(password)
})
},
// @title 职位
// @domain 部门
register(username: string, password: string, domain?: string, title?: string) {
return new Interface(ajax.post, '/api/user/', {
username: username,
password: Base64.encode(password),
domain: domain,
title: title
})
} }
}, },
message: message message: message

@ -9,7 +9,9 @@ import '@/libs/wwLogin.js'
components: {} components: {}
}) })
export default class WxLogin extends Vue { export default class WxLogin extends Vue {
goto(id: string, app: string, url: string, state?: string, href?: string) { goto(id: string, app: string, url: string, state?: number, href?: string) {
// eslint-disable-next-line
// @ts-ignore
window.WwLogin({ window.WwLogin({
id: 'wx_reg', id: 'wx_reg',
appid: id, appid: id,
@ -21,13 +23,13 @@ export default class WxLogin extends Vue {
} }
@Prop({default: ''}) @Prop({default: ''})
aid: '' aid = ''
@Prop({default: ''}) @Prop({default: ''})
app: '' app = ''
@Prop({default: ''}) @Prop({default: ''})
url: '' url = ''
mounted() { mounted() {
this.goto(this.aid, this.app, this.url, new Date().getTime()) this.goto(this.aid, this.app, this.url, new Date().getTime())

@ -0,0 +1,28 @@
<template>
<svg class="icon" aria-hidden="true">
<use :xlink:href="'#icon-'+icon"></use>
</svg>
</template>
<script lang='ts'>
import {Component, Vue} from 'vue-property-decorator'
@Component({
components: {}
})
export default class OneIcon extends Vue {
get icon() {
if (this.$slots.default) return this.$slots.default[0].text?.trim()
console.warn('blank icon name')
return ''
}
}
</script>
<style scoped>
.icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
</style>

@ -0,0 +1,26 @@
import Vue from 'vue'
import OneIcon from './icon.vue'
function loadJS(url: string) {
const script = document.createElement('script')
script.type = 'text/javascript'
script.src = url
document.getElementsByTagName('head')[0].appendChild(script)
}
export default {
installed: false,
install(vue: typeof Vue, options?: { href: '' }): void {
if (this.installed) {
return
}
this.installed = true
if (options && options.href) {
console.log(options.href)
loadJS(options.href)
} else {
console.error('not set iconfont href')
}
vue.component('one-icon', OneIcon)
}
}

@ -4,7 +4,7 @@ function padLeftZero(str: string): string {
const util = { const util = {
title: function (title: string) { title: function (title: string) {
window.document.title = title ? title + ' - Home' : 'veypi project' window.document.title = title ? title + ' - oa' : 'veypi project'
}, },
getCookie(name: string) { getCookie(name: string) {
const reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)') const reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)')

@ -4,7 +4,14 @@ import router from './router'
import store from './store' import store from './store'
import vuetify from './plugins/vuetify' import vuetify from './plugins/vuetify'
import {Api} from '@/api' import {Api} from '@/api'
import OneIcon from '@veypi/one-icon'
import Message from 'vue-m-message'
import 'vue-m-message/dist/index.css'
Vue.use(Message) // will mount `Vue.prototype.$message`
// Vue.use(OneIcon, {href: 'https://at.alicdn.com/t/font_2872366_7aws02sx9bl.js'})
Vue.use(OneIcon, {href: './icon.js'})
Vue.use(Api) Vue.use(Api)
Vue.config.productionTip = false Vue.config.productionTip = false

@ -4,13 +4,23 @@ import Home from '../views/Home.vue'
import Demo from '@/views/demo.vue' import Demo from '@/views/demo.vue'
import Login from '@/views/login.vue' import Login from '@/views/login.vue'
import Register from '@/views/register.vue' import Register from '@/views/register.vue'
import NotFound from '@/views/404.vue'
Vue.use(VueRouter) Vue.use(VueRouter)
// 避免push到相同路径报错
// 获取原型对象上的push函数
const originalPush = VueRouter.prototype.push
// 修改原型对象中的push方法
VueRouter.prototype.push = function push(location: any) {
// eslint-disable-next-line
// @ts-ignore
return originalPush.call(this, location).catch(err => err)
}
const routes: Array<RouteConfig> = [ const routes: Array<RouteConfig> = [
{ {
path: '/', path: '/',
name: 'Home', name: 'home',
component: Home component: Home
}, },
{ {
@ -19,12 +29,12 @@ const routes: Array<RouteConfig> = [
component: Demo component: Demo
}, },
{ {
path: '/login', path: '/login/:uuid?',
name: 'login', name: 'login',
component: Login component: Login
}, },
{ {
path: '/register', path: '/register/:uuid?',
name: 'register', name: 'register',
component: Register component: Register
}, },
@ -32,6 +42,11 @@ const routes: Array<RouteConfig> = [
path: '/wx', path: '/wx',
name: 'wx', name: 'wx',
component: () => import('../views/wx.vue') component: () => import('../views/wx.vue')
},
{
path: '*',
name: '404',
component: NotFound
} }
] ]

@ -1,16 +1,30 @@
import Vue from 'vue' import Vue from 'vue'
import Vuex from 'vuex' import Vuex from 'vuex'
import api from '@/api'
import router from '@/router'
Vue.use(Vuex) Vue.use(Vuex)
export default new Vuex.Store({ export default new Vuex.Store({
state: { state: {
oauuid: '',
user: null user: null
}, },
mutations: { mutations: {
setOA(state: any, data: any) {
state.oauuid = data.uuid
}
}, },
actions: { actions: {
fetchSelf({commit}) {
api.app.self().Start(d => {
commit('setOA', d)
})
},
handleLogout() {
localStorage.removeItem('auth_token')
router.push({name: 'login'})
}
}, },
modules: { modules: {}
}
}) })

@ -0,0 +1,24 @@
<style>
</style>
<template>
<div class='home d-flex justify-center align-center'>
<one-icon style="font-size: 100px">404</one-icon>
</div>
</template>
<script lang='ts'>
import {Component, Vue} from 'vue-property-decorator'
import util from '@/libs/util'
@Component({
components: {}
})
export default class NotFound extends Vue {
mounted() {
}
created() {
util.title('404')
}
}
</script>

@ -6,20 +6,38 @@
</style> </style>
<template> <template>
<div class='home d-flex justify-center align-center'> <div class='home d-flex justify-center align-center'>
<one-icon style="color: aqua;font-size: 50px">glassdoor</one-icon>
</div> </div>
</template> </template>
<script lang='ts'> <script lang='ts'>
import {Component, Vue} from 'vue-property-decorator' import {Component, Vue} from 'vue-property-decorator'
import util from '@/libs/util'
@Component({ @Component({
components: {} components: {}
}) })
export default class Home extends Vue { export default class Home extends Vue {
apps = []
getApps() {
this.$api.app.list().Start(d => {
console.log(d)
this.apps = d
})
}
mounted() { mounted() {
this.getApps()
} }
created() { created() {
} }
beforeCreate() {
if (!util.checkLogin()) {
this.$router.push({name: 'login', query: this.$route.query, params: this.$route.params})
}
}
} }
</script> </script>

@ -54,7 +54,8 @@
<v-card-actions> <v-card-actions>
<v-spacer/> <v-spacer/>
<v-btn type="primary" @click="handleSubmit"></v-btn> <v-btn type="primary" @click="handleSubmit"></v-btn>
<router-link to="/register" style="text-decoration: none;"> <router-link :to="{name: 'register', query:$route.query, params: $route.params}"
style="text-decoration: none;">
<v-btn type="primary" style="margin-left:8px">注册</v-btn> <v-btn type="primary" style="margin-left:8px">注册</v-btn>
</router-link> </router-link>
</v-card-actions> </v-card-actions>
@ -87,13 +88,26 @@ export default class Login extends Vue {
] ]
} }
get app_uuid() {
return this.$route.params.uuid || this.$store.state.oauuid
}
handleSubmit() { handleSubmit() {
this.$api.auth.login(this.formInline.user, this.formInline.password).Start( // eslint-disable-next-line
// @ts-ignore
if (!this.$refs.form.validate()) {
return
}
this.$api.user.login(this.formInline.user, this.formInline.password, this.app_uuid).Start(
data => { data => {
console.log(data)
if (util.checkLogin()) { if (util.checkLogin()) {
// this.$message.success('') // this.$message.success('')
// EventBus.$emit('login', true) // EventBus.$emit('login', true)
this.$nextTick(() => { this.$nextTick(() => {
if (this.$route.query.redirect) {
window.location.href = this.$route.query.redirect as string
}
this.$router.push({name: 'home'}) this.$router.push({name: 'home'})
}) })
} else { } else {
@ -105,12 +119,5 @@ export default class Login extends Vue {
} }
) )
} }
mounted() {
}
created() {
console.log(this.formInline)
}
} }
</script> </script>

@ -90,20 +90,26 @@ export default class Register extends Vue {
] ]
} }
get app_uuid() {
return this.$route.params.uuid || this.$store.state.oauuid
}
handleSubmit() { handleSubmit() {
// eslint-disable-next-line
// @ts-ignore
if (!this.$refs.form.validate()) { if (!this.$refs.form.validate()) {
return return
} }
this.$api.user.register(this.form.username, this.form.passwd).Start( this.$api.user.register(this.form.username, this.form.passwd, this.app_uuid).Start(
(data) => { (data) => {
// this.$message.success('!') this.$message.success('注册成功!')
this.$router.push({name: 'login'}) this.$router.push({name: 'login', params: this.$route.params, query: this.$route.query})
}, },
(data) => { (data) => {
if (data && data.code === '31011') { if (data && data.code === '31011') {
// this.$message.error('') this.$message.error('用户名重复')
} else { } else {
// this.$message.error('') this.$message.error('注册失败')
} }
} }
) )

@ -1155,6 +1155,16 @@
semver "^7.3.2" semver "^7.3.2"
tsutils "^3.17.1" tsutils "^3.17.1"
"@veypi/one-icon@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@veypi/one-icon/-/one-icon-1.0.1.tgz#138adbbcf0738ac40cad44552a20e211da8087c6"
integrity sha512-VEjK/SRpGTYKaqXu6FAVUXZtQy4hdZ886OI2eoMqMfMnt1pPExr9Vjz2NQOL1DZgMGT3mwlQHJyR3iJdz/eE1w==
dependencies:
core-js "^3.6.5"
vue "^2.6.11"
vue-class-component "^7.2.3"
vue-property-decorator "^9.1.2"
"@vue/babel-helper-vue-jsx-merge-props@^1.2.1": "@vue/babel-helper-vue-jsx-merge-props@^1.2.1":
version "1.2.1" version "1.2.1"
resolved "https://registry.npm.taobao.org/@vue/babel-helper-vue-jsx-merge-props/download/@vue/babel-helper-vue-jsx-merge-props-1.2.1.tgz?cache=0&sync_timestamp=1602853295839&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40vue%2Fbabel-helper-vue-jsx-merge-props%2Fdownload%2F%40vue%2Fbabel-helper-vue-jsx-merge-props-1.2.1.tgz#31624a7a505fb14da1d58023725a4c5f270e6a81" resolved "https://registry.npm.taobao.org/@vue/babel-helper-vue-jsx-merge-props/download/@vue/babel-helper-vue-jsx-merge-props-1.2.1.tgz?cache=0&sync_timestamp=1602853295839&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40vue%2Fbabel-helper-vue-jsx-merge-props%2Fdownload%2F%40vue%2Fbabel-helper-vue-jsx-merge-props-1.2.1.tgz#31624a7a505fb14da1d58023725a4c5f270e6a81"
@ -8876,6 +8886,11 @@ vue-loader@^15.9.2:
vue-hot-reload-api "^2.3.0" vue-hot-reload-api "^2.3.0"
vue-style-loader "^4.1.0" vue-style-loader "^4.1.0"
vue-m-message@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/vue-m-message/-/vue-m-message-3.1.0.tgz#8061720ac777b624fad95375594ea142c7d55dc7"
integrity sha512-Mu9ykD7vrUFfhGWTbevHoX1JNTAkOl6X6jDzqVoF5eB9dQHG4jSLmKwlHMZLlnT45lwwSze6vuvi8q5dxCWLQw==
vue-property-decorator@^9.1.2: vue-property-decorator@^9.1.2:
version "9.1.2" version "9.1.2"
resolved "https://registry.npm.taobao.org/vue-property-decorator/download/vue-property-decorator-9.1.2.tgz#266a2eac61ba6527e2e68a6933cfb98fddab5457" resolved "https://registry.npm.taobao.org/vue-property-decorator/download/vue-property-decorator-9.1.2.tgz#266a2eac61ba6527e2e68a6933cfb98fddab5457"

@ -0,0 +1,54 @@
package sub
import (
"OneAuth/cfg"
"OneAuth/models"
"github.com/urfave/cli/v2"
"github.com/veypi/utils"
"github.com/veypi/utils/log"
)
var App = &cli.Command{
Name: "app",
Subcommands: []*cli.Command{
{
Name: "list",
Action: runAppList,
},
{
Name: "create",
Action: runAppCreate,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "name",
Required: true,
},
},
},
},
}
func runAppList(c *cli.Context) error {
list := make([]*models.App, 0, 10)
err := cfg.DB().Find(&list).Error
if err != nil {
return err
}
for _, a := range list {
log.Info().Msgf("%d: %s", a.ID, a.Name)
}
return nil
}
func runAppCreate(c *cli.Context) error {
app := &models.App{}
app.Name = c.String("name")
app.Key = utils.RandSeq(16)
app.UUID = utils.RandSeq(8)
err := cfg.DB().Create(app).Error
if err != nil {
return err
}
log.Info().Msgf("app: %s\nuuid: %s\nkey: %s", app.Name, app.UUID, app.Key)
return nil
}

@ -0,0 +1,131 @@
package sub
import (
"OneAuth/cfg"
"OneAuth/libs/auth"
"OneAuth/models"
"github.com/urfave/cli/v2"
"github.com/veypi/utils/cmd"
"github.com/veypi/utils/log"
"strconv"
)
var Init = &cli.Command{
Name: "init",
Action: runInit,
}
func runInit(c *cli.Context) error {
return InitSystem()
}
// 初始化项目
func InitSystem() error {
db()
self, err := selfApp()
if err != nil {
return err
}
cfg.CFG.APPID = self.ID
cfg.CFG.APPKey = self.Key
err = cmd.DumpCfg(cfg.Path, cfg.CFG)
// TODO
//if err != nil {
// return err
//}
err = role(self.InitRoleID == 0)
return nil
}
func db() {
db := cfg.DB()
log.HandlerErrs(
db.SetupJoinTable(&models.User{}, "Roles", &models.UserRole{}),
db.SetupJoinTable(&models.Role{}, "Users", &models.UserRole{}),
db.SetupJoinTable(&models.User{}, "Apps", &models.AppUser{}),
db.SetupJoinTable(&models.App{}, "Users", &models.AppUser{}),
db.AutoMigrate(&models.User{}, &models.Role{}, &models.Auth{}, &models.App{}),
)
log.HandlerErrs(
db.AutoMigrate(&models.Wechat{}, &models.Resource{}),
)
}
func selfApp() (*models.App, error) {
self := &models.App{
Name: "OA",
Icon: "",
UUID: "jU5Jo5hM",
Des: "",
Creator: 0,
UserCount: 0,
Hide: false,
Host: "",
UserRefreshUrl: "/",
Key: "cB43wF94MLTksyBK",
EnableRegister: true,
EnableUserKey: true,
EnableUser: true,
EnableWx: false,
EnablePhone: false,
EnableEmail: false,
Wx: nil,
}
return self, cfg.DB().Where("uuid = ?", self.UUID).FirstOrCreate(self).Error
}
func role(reset_init_role bool) error {
authMap := make(map[string]*models.Resource)
n := []string{
auth.APP,
auth.User,
auth.Res,
auth.Auth,
auth.Role,
}
var err error
adminRole := &models.Role{
AppID: cfg.CFG.APPID,
Name: "admin",
IsUnique: false,
}
err = cfg.DB().Where(adminRole).FirstOrCreate(adminRole).Error
if err != nil {
return err
}
for _, na := range n {
a := &models.Resource{
AppID: cfg.CFG.APPID,
Name: na,
Tag: "",
Des: "",
}
err = cfg.DB().Where(a).FirstOrCreate(a).Error
if err != nil {
return err
}
authMap[na] = a
err = auth.BindRoleAuth(cfg.DB(), adminRole.ID, a.ID, models.AuthAll, "")
if err != nil {
return err
}
}
userRole := &models.Role{
AppID: cfg.CFG.APPID,
Name: "user",
IsUnique: false,
}
err = cfg.DB().Where(userRole).FirstOrCreate(userRole).Error
if err != nil {
return err
}
err = auth.BindRoleAuth(cfg.DB(), userRole.ID, authMap[auth.APP].ID, models.AuthRead, strconv.Itoa(int(cfg.CFG.APPID)))
if err != nil {
return err
}
if reset_init_role {
return cfg.DB().Model(&models.App{}).Where("id = ?", cfg.CFG.APPID).Update("init_role_id", adminRole.ID).Error
}
return nil
}

@ -0,0 +1,114 @@
package sub
import (
"OneAuth/cfg"
"OneAuth/models"
"github.com/urfave/cli/v2"
"github.com/veypi/utils/log"
)
var Role = &cli.Command{
Name: "role",
Usage: "",
Description: "",
Subcommands: []*cli.Command{
{
Name: "list",
Action: runRoleList,
},
{
Name: "create",
Action: runRoleCreate,
Flags: []cli.Flag{
&cli.UintFlag{
Name: "id",
Usage: "app id",
Required: true,
},
&cli.StringFlag{
Name: "name",
Usage: "role name",
Required: true,
},
},
},
},
Flags: []cli.Flag{},
}
func runRoleList(c *cli.Context) error {
roles := make([]*models.Role, 0, 10)
err := cfg.DB().Find(&roles).Error
if err != nil {
return err
}
for _, r := range roles {
log.Info().Msgf("%d %s@%d", r.ID, r.Name, r.AppID)
}
return nil
}
func runRoleCreate(c *cli.Context) error {
id := c.Uint("id")
name := c.String("name")
rl := &models.Role{}
rl.AppID = id
rl.Name = name
err := cfg.DB().Where(rl).FirstOrCreate(rl).Error
return err
}
var Resource = &cli.Command{
Name: "resource",
Usage: "resource manual",
Subcommands: []*cli.Command{
{
Name: "list",
Action: runResourceList,
Flags: []cli.Flag{
&cli.UintFlag{
Name: "id",
Usage: "app id",
},
},
},
{
Name: "create",
Action: runResourceCreate,
Flags: []cli.Flag{
&cli.UintFlag{
Name: "id",
Usage: "app id",
Required: true,
},
&cli.StringFlag{
Name: "name",
Usage: "role name",
Required: true,
},
},
},
},
}
func runResourceList(c *cli.Context) error {
query := &models.Resource{}
query.AppID = c.Uint("id")
l := make([]*models.Resource, 0, 10)
err := cfg.DB().Where(query).Find(&l).Error
if err != nil {
return nil
}
for _, r := range l {
log.Info().Msgf("%d: %s@%d", r.ID, r.Name, r.AppID)
}
return nil
}
func runResourceCreate(c *cli.Context) error {
query := &models.Resource{}
query.AppID = c.Uint("id")
query.Name = c.String("name")
err := cfg.DB().Where(query).FirstOrCreate(query).Error
return err
}

@ -3,7 +3,6 @@ package sub
import ( import (
"OneAuth/api" "OneAuth/api"
"OneAuth/cfg" "OneAuth/cfg"
"OneAuth/models"
"embed" "embed"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"github.com/veypi/OneBD" "github.com/veypi/OneBD"
@ -20,7 +19,7 @@ var staticFiles embed.FS
//go:embed static/index.html //go:embed static/index.html
var indexFile []byte var indexFile []byte
var Web = cli.Command{ var Web = &cli.Command{
Name: "web", Name: "web",
Usage: "", Usage: "",
Description: "oa 核心http服务", Description: "oa 核心http服务",
@ -29,7 +28,6 @@ var Web = cli.Command{
} }
func RunWeb(c *cli.Context) error { func RunWeb(c *cli.Context) error {
_ = runSyncDB(c)
ll := log.InfoLevel ll := log.InfoLevel
if l, err := log.ParseLevel(cfg.CFG.LoggerLevel); err == nil { if l, err := log.ParseLevel(cfg.CFG.LoggerLevel); err == nil {
ll = l ll = l
@ -70,16 +68,3 @@ func RunWeb(c *cli.Context) error {
log.Info().Msg("\nRouting Table\n" + app.Router().String()) log.Info().Msg("\nRouting Table\n" + app.Router().String())
return app.Run() return app.Run()
} }
func runSyncDB(*cli.Context) error {
db := cfg.DB()
log.HandlerErrs(
db.SetupJoinTable(&models.User{}, "Roles", &models.UserRole{}),
db.SetupJoinTable(&models.Role{}, "Users", &models.UserRole{}),
db.AutoMigrate(&models.User{}, &models.Role{}, &models.Auth{}),
)
log.HandlerErrs(
db.AutoMigrate(&models.App{}, &models.Wechat{}),
)
return nil
}

Loading…
Cancel
Save