feat: (rbac) permission validate / user tags
This commit is contained in:
parent
dc8bd1cd1b
commit
8c013a613b
|
@ -0,0 +1,117 @@
|
||||||
|
package biz
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"infini.sh/framework/core/global"
|
||||||
|
"infini.sh/framework/core/util"
|
||||||
|
"net/http"
|
||||||
|
"src/github.com/golang-jwt/jwt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserClaims struct {
|
||||||
|
*jwt.RegisteredClaims
|
||||||
|
*User
|
||||||
|
}
|
||||||
|
type User struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
UserId string `json:"user_id"`
|
||||||
|
ApiPermission map[string]struct{} `json:"api_permission"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const Secret = "console"
|
||||||
|
|
||||||
|
func Login(username string, password string) (m map[string]interface{}, err error) {
|
||||||
|
|
||||||
|
u, _ := global.Env().GetConfig("bootstrap.username", "admin")
|
||||||
|
p, _ := global.Env().GetConfig("bootstrap.password", "admin")
|
||||||
|
|
||||||
|
if u != username || p != password {
|
||||||
|
err = errors.New("invalid username or password")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, UserClaims{
|
||||||
|
User: &User{
|
||||||
|
Username: u,
|
||||||
|
UserId: "admin",
|
||||||
|
ApiPermission: map[string]struct{}{
|
||||||
|
"account.profile": struct{}{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RegisteredClaims: &jwt.RegisteredClaims{
|
||||||
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
tokenString, err := token.SignedString([]byte(Secret))
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m = util.MapStr{
|
||||||
|
"access_token": tokenString,
|
||||||
|
"username": u,
|
||||||
|
"userid": "admin",
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidateLogin(authorizationHeader string) (clams *UserClaims, err error) {
|
||||||
|
|
||||||
|
if authorizationHeader == "" {
|
||||||
|
err = errors.New("authorization header is empty")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fields := strings.Fields(authorizationHeader)
|
||||||
|
if fields[0] != "Bearer" || len(fields) != 2 {
|
||||||
|
err = errors.New("authorization header is invalid")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tokenString := fields[1]
|
||||||
|
token, err := jwt.ParseWithClaims(tokenString, &UserClaims{}, func(token *jwt.Token) (interface{}, error) {
|
||||||
|
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||||
|
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(Secret), nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if clams, ok := token.Claims.(*UserClaims); ok && token.Valid {
|
||||||
|
return clams, nil
|
||||||
|
}
|
||||||
|
if clams.UserId == "" {
|
||||||
|
err = errors.New("user id is empty")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
func ValidatePermission(r *http.Request, permissions string) (err error) {
|
||||||
|
reqUser, err := FromUserContext(r.Context())
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if reqUser.UserId == "" {
|
||||||
|
err = errors.New("user id is empty")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if reqUser.ApiPermission == nil {
|
||||||
|
err = errors.New("api permission is empty")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := reqUser.ApiPermission[permissions]; !ok {
|
||||||
|
err = errors.New("permission denied")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package biz
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ctxUserKey = "user"
|
||||||
|
|
||||||
|
func NewUserContext(ctx context.Context, clam *UserClaims) context.Context {
|
||||||
|
return context.WithValue(ctx, ctxUserKey, clam)
|
||||||
|
}
|
||||||
|
func FromUserContext(ctx context.Context) (*User, error) {
|
||||||
|
ctxUser := ctx.Value(ctxUserKey)
|
||||||
|
if ctxUser == nil {
|
||||||
|
return nil, errors.New("user not found")
|
||||||
|
}
|
||||||
|
reqUser, ok := ctxUser.(*UserClaims)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("invalid context user")
|
||||||
|
}
|
||||||
|
return reqUser.User, nil
|
||||||
|
}
|
||||||
|
func NewPermissionContext(ctx context.Context) {
|
||||||
|
|
||||||
|
}
|
||||||
|
func FromPermissionContext(ctx context.Context) {
|
||||||
|
|
||||||
|
}
|
|
@ -1,8 +1,38 @@
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
func LoginRequired() {
|
import (
|
||||||
|
"infini.sh/console/internal/biz"
|
||||||
|
httprouter "infini.sh/framework/core/api/router"
|
||||||
|
"infini.sh/framework/core/util"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
}
|
func LoginRequired(h httprouter.Handle) httprouter.Handle {
|
||||||
func PermissionRequired() {
|
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
|
|
||||||
|
claims, err := biz.ValidateLogin(r.Header.Get("Authorization"))
|
||||||
|
if err != nil {
|
||||||
|
w = handleError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r = r.WithContext(biz.NewUserContext(r.Context(), claims))
|
||||||
|
h(w, r, ps)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func PermissionRequired(h httprouter.Handle, permissions string) httprouter.Handle {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
|
err := biz.ValidatePermission(r, permissions)
|
||||||
|
if err != nil {
|
||||||
|
w = handleError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
h(w, r, ps)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func handleError(w http.ResponseWriter, err error) http.ResponseWriter {
|
||||||
|
w.Header().Set("Content-type", util.ContentTypeJson)
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
w.Write([]byte(`{"error":"` + err.Error() + `"}`))
|
||||||
|
return w
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package rbac
|
package rbac
|
||||||
|
|
||||||
import "infini.sh/framework/core/orm"
|
import (
|
||||||
|
"infini.sh/framework/core/orm"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type Role struct {
|
type Role struct {
|
||||||
orm.ORMObjectBase
|
orm.ORMObjectBase
|
||||||
|
@ -20,3 +23,28 @@ type ElasticsearchPermission struct {
|
||||||
ClusterPrivilege []string `json:"cluster_privilege" elastic_mapping:"cluster_privilege:{type:object}"`
|
ClusterPrivilege []string `json:"cluster_privilege" elastic_mapping:"cluster_privilege:{type:object}"`
|
||||||
IndexPrivilege []string `json:"index_privilege" elastic_mapping:"index_privilege:{type:object}"`
|
IndexPrivilege []string `json:"index_privilege" elastic_mapping:"index_privilege:{type:object}"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ConsoleOperate struct {
|
||||||
|
UserId string `json:"user_id" elastic_mapping:"user_id:{type:keyword}"`
|
||||||
|
}
|
||||||
|
type Operation struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Timestamp time.Time `json:"timestamp"`
|
||||||
|
Metadata struct {
|
||||||
|
Labels struct {
|
||||||
|
Userid string `json:"userid"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
} `json:"labels"`
|
||||||
|
Category string `json:"category"`
|
||||||
|
Group string `json:"group"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
} `json:"metadata"`
|
||||||
|
Changelog []struct {
|
||||||
|
From string `json:"from"`
|
||||||
|
Path []string `json:"path"`
|
||||||
|
To string `json:"to"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
} `json:"changelog"`
|
||||||
|
Payload interface{} `json:"payload"`
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ type User struct {
|
||||||
Phone string `json:"phone" elastic_mapping:"phone:{type:keyword}"`
|
Phone string `json:"phone" elastic_mapping:"phone:{type:keyword}"`
|
||||||
Email string `json:"email" elastic_mapping:"email:{type:keyword}"`
|
Email string `json:"email" elastic_mapping:"email:{type:keyword}"`
|
||||||
Roles []UserRole `json:"roles" elastic_mapping:"roles:{type:text}"`
|
Roles []UserRole `json:"roles" elastic_mapping:"roles:{type:text}"`
|
||||||
|
Tags []string `json:"tags" elastic_mapping:"tags:{type:text}"`
|
||||||
}
|
}
|
||||||
type UserRole struct {
|
type UserRole struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
package account
|
package account
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"infini.sh/console/internal/biz"
|
||||||
|
"infini.sh/console/internal/dto"
|
||||||
|
m "infini.sh/console/internal/middleware"
|
||||||
"infini.sh/framework/core/api"
|
"infini.sh/framework/core/api"
|
||||||
"infini.sh/framework/core/api/router"
|
"infini.sh/framework/core/api/router"
|
||||||
"infini.sh/framework/core/global"
|
|
||||||
"infini.sh/framework/core/util"
|
"infini.sh/framework/core/util"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
@ -14,54 +16,35 @@ type Account struct {
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
account := Account{}
|
account := Account{}
|
||||||
api.HandleAPIMethod(api.POST, "/account/login", account.AccountLogin)
|
api.HandleAPIMethod(api.POST, "/account/login", account.Login)
|
||||||
|
|
||||||
api.HandleAPIMethod(api.GET, "/account/current_user", account.CurrentUser)
|
api.HandleAPIMethod(api.GET, "/account/current_user", account.CurrentUser)
|
||||||
|
|
||||||
|
api.HandleAPIMethod(api.DELETE, "/account/logout", account.Logout)
|
||||||
|
api.HandleAPIMethod(api.GET, "/account/profile",
|
||||||
|
m.LoginRequired(account.Profile))
|
||||||
}
|
}
|
||||||
|
|
||||||
var userInSession string="user_in_session"
|
const userInSession = "user_in_session"
|
||||||
|
|
||||||
func (handler Account)AccountLogin(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
|
func (h Account) Login(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
|
|
||||||
//{"userName":"admin","password":"111111","type":"account"}
|
var req dto.Login
|
||||||
|
err := h.DecodeJSON(r, &req)
|
||||||
json,err:=handler.GetJSON(req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handler.Error(w,err)
|
h.Error(w, err)
|
||||||
return
|
|
||||||
}
|
|
||||||
userName,err:=json.String("userName")
|
|
||||||
if err!=nil{
|
|
||||||
handler.Error(w,err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
password,err:=json.String("password")
|
|
||||||
if err!=nil{
|
|
||||||
handler.Error(w,err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
u,_:=global.Env().GetConfig("bootstrap.username","admin")
|
data, err := biz.Login(req.Username, req.Password)
|
||||||
p,_:=global.Env().GetConfig("bootstrap.password","admin")
|
if err != nil {
|
||||||
if u==userName&&p==password{
|
h.Error(w, err)
|
||||||
data := util.MapStr{
|
return
|
||||||
"status": "ok",
|
|
||||||
"type": "account",
|
|
||||||
"currentAuthority": "admin",
|
|
||||||
"userid": "10001",
|
|
||||||
}
|
|
||||||
api.SetSession(w,req, userInSession,userName)
|
|
||||||
handler.WriteJSON(w, data, http.StatusOK)
|
|
||||||
}else{
|
|
||||||
data := util.MapStr{
|
|
||||||
"status": "error",
|
|
||||||
"type": "account",
|
|
||||||
"currentAuthority": "guest",
|
|
||||||
}
|
|
||||||
handler.WriteJSON(w, data, http.StatusOK)
|
|
||||||
}
|
}
|
||||||
|
h.WriteJSON(w, data, http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler Account)CurrentUser(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
|
func (h Account) CurrentUser(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
|
||||||
|
|
||||||
exists, user := api.GetSession(w, req, userInSession)
|
exists, user := api.GetSession(w, req, userInSession)
|
||||||
if exists {
|
if exists {
|
||||||
|
@ -94,13 +77,25 @@ func (handler Account)CurrentUser(w http.ResponseWriter, req *http.Request, ps h
|
||||||
"phone": "4001399200",
|
"phone": "4001399200",
|
||||||
}
|
}
|
||||||
|
|
||||||
handler.WriteJSON(w, data,200)
|
h.WriteJSON(w, data, 200)
|
||||||
} else {
|
} else {
|
||||||
data := util.MapStr{
|
data := util.MapStr{
|
||||||
"status": "error",
|
"status": "error",
|
||||||
"type": "account",
|
"type": "account",
|
||||||
"currentAuthority": "guest",
|
"currentAuthority": "guest",
|
||||||
}
|
}
|
||||||
handler.WriteJSON(w, data, 403)
|
h.WriteJSON(w, data, 403)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func (h Account) Logout(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
|
|
||||||
|
}
|
||||||
|
func (h Account) Profile(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
|
reqUser, err := biz.FromUserContext(r.Context())
|
||||||
|
if err != nil {
|
||||||
|
h.Error(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
h.WriteJSON(w, reqUser, 200)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package rbac
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"infini.sh/console/internal/biz"
|
"infini.sh/console/internal/biz"
|
||||||
|
m "infini.sh/console/internal/middleware"
|
||||||
|
|
||||||
"infini.sh/framework/core/api"
|
"infini.sh/framework/core/api"
|
||||||
"infini.sh/framework/core/util"
|
"infini.sh/framework/core/util"
|
||||||
|
@ -18,10 +19,10 @@ func registerRouter() {
|
||||||
r := Rbac{}
|
r := Rbac{}
|
||||||
api.HandleAPIMethod(api.GET, "/permission/:type", r.ListPermission)
|
api.HandleAPIMethod(api.GET, "/permission/:type", r.ListPermission)
|
||||||
api.HandleAPIMethod(api.POST, "/role/:type", r.CreateRole)
|
api.HandleAPIMethod(api.POST, "/role/:type", r.CreateRole)
|
||||||
api.HandleAPIMethod(api.GET, "/role/:id", r.GetRole)
|
api.HandleAPIMethod(api.GET, "/role/:id", m.LoginRequired(r.GetRole))
|
||||||
api.HandleAPIMethod(api.DELETE, "/role/:id", r.DeleteRole)
|
api.HandleAPIMethod(api.DELETE, "/role/:id", r.DeleteRole)
|
||||||
api.HandleAPIMethod(api.PUT, "/role/:id", r.UpdateRole)
|
api.HandleAPIMethod(api.PUT, "/role/:id", r.UpdateRole)
|
||||||
api.HandleAPIMethod(api.GET, "/role/_search", r.SearchRole)
|
api.HandleAPIMethod(api.GET, "/role/_search", m.LoginRequired(m.PermissionRequired(r.SearchRole, "search.role")))
|
||||||
|
|
||||||
api.HandleAPIMethod(api.POST, "/user", r.CreateUser)
|
api.HandleAPIMethod(api.POST, "/user", r.CreateUser)
|
||||||
api.HandleAPIMethod(api.GET, "/user/:id", r.GetUser)
|
api.HandleAPIMethod(api.GET, "/user/:id", r.GetUser)
|
||||||
|
|
Loading…
Reference in New Issue