Skip to content

Commit

Permalink
Merge pull request #18 from go-cinch/piupuer/dev
Browse files Browse the repository at this point in the history
[fix]bug
  • Loading branch information
piupuer authored Oct 15, 2024
2 parents d7d841e + e67ec01 commit 007be94
Show file tree
Hide file tree
Showing 12 changed files with 209 additions and 28 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ require (
github.com/go-cinch/common/i18n v1.0.6
github.com/go-cinch/common/id v1.0.4
github.com/go-cinch/common/idempotent v1.0.4
github.com/go-cinch/common/jwt v1.0.3
github.com/go-cinch/common/jwt v1.0.4
github.com/go-cinch/common/log v1.1.1
github.com/go-cinch/common/middleware/i18n v1.0.5
github.com/go-cinch/common/middleware/logging v1.0.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ github.com/go-cinch/common/id v1.0.4 h1:bMjcxMq5lL19o2a/ty6OnS8tIn7kc98FznAq2vyp
github.com/go-cinch/common/id v1.0.4/go.mod h1:ZeuOCZyx1ZKuCexzLRW0gBCnwDUKH8XDs1S0dNv4Z+k=
github.com/go-cinch/common/idempotent v1.0.4 h1:E/Ab+FHWjs1ym0U0mips6MyHEzucEdedmVWA22U3du8=
github.com/go-cinch/common/idempotent v1.0.4/go.mod h1:t4rBikz+RhpmF0tdGx+rm0Z1FzDy/6GtelgFB7RlaiY=
github.com/go-cinch/common/jwt v1.0.3 h1:/jznvjevW+2KsmWm666O1NxkHLSfVmrjoWwkuWAqewk=
github.com/go-cinch/common/jwt v1.0.3/go.mod h1:++p7kddWh9SyCYIYWEb5mfiR+ljKC3I5puiyjz7oGNs=
github.com/go-cinch/common/jwt v1.0.4 h1:PWLIQ/HaMhQSkyUafBCjQDNXwz1mzeCzsTCbgeKEeRI=
github.com/go-cinch/common/jwt v1.0.4/go.mod h1:++p7kddWh9SyCYIYWEb5mfiR+ljKC3I5puiyjz7oGNs=
github.com/go-cinch/common/log v1.1.1 h1:9ot3Qw4BKDfOZ7IC/3ZreuYJb953YD7pmM6ev6xT9FY=
github.com/go-cinch/common/log v1.1.1/go.mod h1:O4k/ArEdZS6c+YPKdESWJnVfhQ0QlwtIQ6mYxJZt5po=
github.com/go-cinch/common/middleware/i18n v1.0.5 h1:SmxEMljqYF8xraaaa3KZgvNbKIE06ShQAZ34w87wkvs=
Expand Down
4 changes: 2 additions & 2 deletions internal/biz/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ type ActionRepo interface {
Update(ctx context.Context, item *UpdateAction) error
Delete(ctx context.Context, ids ...uint64) error
CodeExists(ctx context.Context, code string) error
Permission(ctx context.Context, code string, req CheckPermission) bool
MatchResource(ctx context.Context, resource string, req CheckPermission) bool
Permission(ctx context.Context, code string, req *CheckPermission) bool
MatchResource(ctx context.Context, resource string, req *CheckPermission) bool
}

type ActionUseCase struct {
Expand Down
4 changes: 2 additions & 2 deletions internal/biz/permission.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type CheckPermission struct {
}

type PermissionRepo interface {
Check(ctx context.Context, item CheckPermission) bool
Check(ctx context.Context, item *CheckPermission) bool
GetByUserCode(ctx context.Context, code string) *Permission
}

Expand All @@ -36,7 +36,7 @@ func NewPermissionUseCase(c *conf.Bootstrap, repo PermissionRepo) *PermissionUse
}
}

func (uc *PermissionUseCase) Check(ctx context.Context, item CheckPermission) (rp bool) {
func (uc *PermissionUseCase) Check(ctx context.Context, item *CheckPermission) (rp bool) {
rp = uc.repo.Check(ctx, item)
return
}
Expand Down
12 changes: 7 additions & 5 deletions internal/biz/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ type UserRepo interface {
Update(ctx context.Context, item *UpdateUser) error
Delete(ctx context.Context, ids ...uint64) error
LastLogin(ctx context.Context, username string) error
WrongPwd(ctx context.Context, req LoginTime) error
WrongPwd(ctx context.Context, req *LoginTime) error
UpdatePassword(ctx context.Context, item *User) error
IdExists(ctx context.Context, id uint64) error
}
Expand Down Expand Up @@ -211,7 +211,7 @@ func (uc *UserUseCase) find(ctx context.Context, action string, condition *FindU

func (uc *UserUseCase) InfoFromCtx(ctx context.Context) (rp *UserInfo) {
user := jwt.FromServerContext(ctx)
return uc.Info(ctx, user.Code)
return uc.Info(ctx, user.Attrs["code"])
}

func (uc *UserUseCase) Info(ctx context.Context, code string) (rp *UserInfo) {
Expand Down Expand Up @@ -259,8 +259,10 @@ func (uc *UserUseCase) Login(ctx context.Context, item *Login) (rp *LoginToken,
return
}
authUser := jwt.User{
Code: status.Code,
Platform: status.Platform,
Attrs: map[string]string{
"code": status.Code,
"platform": status.Platform,
},
}
token, expireTime := authUser.CreateToken(uc.c.Server.Jwt.Key, uc.c.Server.Jwt.Expires)
rp.Token = token
Expand All @@ -279,7 +281,7 @@ func (uc *UserUseCase) LastLogin(ctx context.Context, username string) error {
})
}

func (uc *UserUseCase) WrongPwd(ctx context.Context, req LoginTime) error {
func (uc *UserUseCase) WrongPwd(ctx context.Context, req *LoginTime) error {
return uc.tx.Tx(ctx, func(ctx context.Context) (err error) {
err = uc.repo.WrongPwd(ctx, req)
if err != nil {
Expand Down
5 changes: 2 additions & 3 deletions internal/biz/whitelist.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
const (
WhitelistPermissionCategory uint32 = iota
WhitelistJwtCategory
WhitelistIdempotentCategory
)

type Whitelist struct {
Expand All @@ -28,8 +27,8 @@ type FindWhitelist struct {
}

type HasWhitelist struct {
Category uint32 `json:"category"`
Permission CheckPermission `json:"permission"`
Category uint32 `json:"category"`
Permission *CheckPermission `json:"permission"`
}

type FindWhitelistCache struct {
Expand Down
6 changes: 3 additions & 3 deletions internal/data/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ func (ro actionRepo) WordExists(ctx context.Context, word string) (ok bool) {
return
}

func (ro actionRepo) Permission(ctx context.Context, code string, req biz.CheckPermission) (pass bool) {
func (ro actionRepo) Permission(ctx context.Context, code string, req *biz.CheckPermission) (pass bool) {
arr := strings.Split(code, ",")
for _, item := range arr {
pass = ro.permission(ctx, item, req)
Expand All @@ -195,15 +195,15 @@ func (ro actionRepo) Permission(ctx context.Context, code string, req biz.CheckP
return
}

func (ro actionRepo) permission(ctx context.Context, code string, req biz.CheckPermission) (pass bool) {
func (ro actionRepo) permission(ctx context.Context, code string, req *biz.CheckPermission) (pass bool) {
if code == "" {
return
}
action := ro.hotspot.GetActionByCode(ctx, code)
return ro.MatchResource(ctx, action.Resource, req)
}

func (actionRepo) MatchResource(_ context.Context, resource string, req biz.CheckPermission) (pass bool) {
func (actionRepo) MatchResource(_ context.Context, resource string, req *biz.CheckPermission) (pass bool) {
if resource == "" {
// empty resource no need match
return
Expand Down
2 changes: 1 addition & 1 deletion internal/data/permission.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func NewPermissionRepo(data *Data, action biz.ActionRepo, hotspot biz.HotspotRep
}
}

func (ro permissionRepo) Check(ctx context.Context, item biz.CheckPermission) (pass bool) {
func (ro permissionRepo) Check(ctx context.Context, item *biz.CheckPermission) (pass bool) {
user := ro.hotspot.GetUserByCode(ctx, item.UserCode)
// 1. check default permission
defaultAction := ro.hotspot.GetActionByWord(ctx, "default")
Expand Down
2 changes: 1 addition & 1 deletion internal/data/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ func (ro userRepo) LastLogin(ctx context.Context, username string) (err error) {
return
}

func (ro userRepo) WrongPwd(ctx context.Context, req biz.LoginTime) (err error) {
func (ro userRepo) WrongPwd(ctx context.Context, req *biz.LoginTime) (err error) {
oldItem, err := ro.GetByUsername(ctx, req.Username)
if err != nil {
return
Expand Down
2 changes: 1 addition & 1 deletion internal/pkg/task/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func process(t task) (err error) {
case t.c.Task.Group.LoginFailed:
var req biz.LoginTime
utils.Json2Struct(&req, t.payload.Payload)
err = t.user.WrongPwd(ctx, req)
err = t.user.WrongPwd(ctx, &req)
case t.c.Task.Group.LoginLast:
var req biz.LoginTime
utils.Json2Struct(&req, t.payload.Payload)
Expand Down
178 changes: 178 additions & 0 deletions internal/server/middleware/permission.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package middleware

import (
"context"
"errors"
"net/http"
"strings"
"time"

"auth/api/auth"
"auth/internal/biz"
"auth/internal/conf"
"github.com/go-cinch/common/copierx"
jwtLocal "github.com/go-cinch/common/jwt"
"github.com/go-cinch/common/log"
"github.com/go-cinch/common/utils"
"github.com/go-kratos/kratos/v2/middleware"
"github.com/go-kratos/kratos/v2/transport"
kratosHttp "github.com/go-kratos/kratos/v2/transport/http"
jwtV4 "github.com/golang-jwt/jwt/v4"
"github.com/redis/go-redis/v9"
"google.golang.org/protobuf/types/known/emptypb"
)

const (
pubURIPrefix = "/pub/"
jwtTokenCachePrefix = "jwt.token"
jwtTokenCacheExpire = 10 * time.Minute
permissionHeaderMethod = "x-original-method"
permissionHeaderURI = "x-permission-uri"
)

func Permission(c *conf.Bootstrap, client redis.UniversalClient, whitelist *biz.WhitelistUseCase) func(handler middleware.Handler) middleware.Handler {
return func(handler middleware.Handler) middleware.Handler {
return func(ctx context.Context, req interface{}) (rp interface{}, err error) {
tr, _ := transport.FromServerContext(ctx)
if tr.Kind() == transport.KindGRPC {
// 1. grpc api for internal no need check
// tip: u can add ur logic if need check grpc
return handler(ctx, req)
}
// check http api
var uri string
operation := tr.Operation()
if ht, ok := tr.(kratosHttp.Transporter); ok {
uri = ht.Request().URL.Path
}
// 2. public api no need check
if strings.Contains(uri, pubURIPrefix) {
return handler(ctx, req)
}
if operation == auth.OperationAuthPermission && permissionWhitelist(ctx, whitelist, req) {
// for nginx auth_request
// 3. permission whitelist api no need check
rp = &emptypb.Empty{}
return
} else if jwtWhitelist(ctx, whitelist) {
// 4. jwt whitelist api no need check
return handler(ctx, req)
}
user, err := parseJwt(ctx, c, client, c.Server.Jwt.Key)
if err != nil {
return
}
// pass user info into ctx
ctx = jwtLocal.NewServerContextByUser(ctx, *user)
return handler(ctx, req)
}
}
}

func permissionWhitelist(ctx context.Context, whitelist *biz.WhitelistUseCase, req interface{}) (ok bool) {
tr, _ := transport.FromServerContext(ctx)
var r biz.CheckPermission
copierx.Copy(&r, req)
// get from header if exist
method := tr.RequestHeader().Get(permissionHeaderMethod)
if method != "" {
r.Method = method
}
uri := tr.RequestHeader().Get(permissionHeaderURI)
if uri != "" {
r.URI = uri
}
// public api no need check
if strings.Contains(r.URI, pubURIPrefix) {
return true
}
log.
WithContext(ctx).
Info("method: %s, uri: %s, resource: %s", r.Method, r.URI, r.Resource)
// skip options
if r.Method == http.MethodOptions || r.Method == http.MethodHead {
return
}
// check if it is on the whitelist
ok = whitelist.Has(ctx, &biz.HasWhitelist{
Category: biz.WhitelistPermissionCategory,
Permission: &r,
})
// override params
v, ok2 := req.(*auth.PermissionRequest)
if ok2 {
v.Method = &r.Method
v.Uri = &r.URI
req = v
return
}
return
}

func jwtWhitelist(ctx context.Context, whitelist *biz.WhitelistUseCase) bool {
tr, _ := transport.FromServerContext(ctx)
return whitelist.Has(ctx, &biz.HasWhitelist{
Category: biz.WhitelistJwtCategory,
Permission: &biz.CheckPermission{
Resource: tr.Operation(),
},
})
}

func parseJwt(ctx context.Context, c *conf.Bootstrap, client redis.UniversalClient, jwtKey string) (user *jwtLocal.User, err error) {
user = jwtLocal.FromServerContext(ctx)
if user.Token == "" {
err = biz.ErrJwtMissingToken(ctx)
return
}
key := strings.Join([]string{c.Name, jwtTokenCachePrefix, utils.StructMd5(user.Token)}, ".")
res, _ := client.Get(ctx, key).Result()
if res != "" {
utils.Json2Struct(user, res)
return
}

// parse Authorization jwt token to get user info
var info *jwtV4.Token
info, err = parseToken(ctx, jwtKey, user.Token)
if err != nil {
return
}
ctx = jwtLocal.NewServerContext(ctx, info.Claims, "code", "platform")
user = jwtLocal.FromServerContext(ctx)
client.Set(ctx, key, utils.Struct2Json(user), jwtTokenCacheExpire)
return
}

func parseToken(ctx context.Context, key, jwtToken string) (info *jwtV4.Token, err error) {
info, err = jwtV4.Parse(jwtToken, func(token *jwtV4.Token) (rp interface{}, err error) {
rp = []byte(key)
return
})
if err != nil {
var ve *jwtV4.ValidationError
ok := errors.As(err, &ve)
if !ok {
return
}
if ve.Errors&jwtV4.ValidationErrorMalformed != 0 {
err = biz.ErrJwtTokenInvalid(ctx)
return
}
if ve.Errors&(jwtV4.ValidationErrorExpired|jwtV4.ValidationErrorNotValidYet) != 0 {
err = biz.ErrJwtTokenExpired(ctx)
return
}
err = biz.ErrJwtTokenParseFail(ctx)
return
}
if !info.Valid {
err = biz.ErrJwtTokenParseFail(ctx)
return
}
if info.Method != jwtV4.SigningMethodHS512 {
err = biz.ErrJwtUnSupportSigningMethod(ctx)
return
}
return
}
16 changes: 9 additions & 7 deletions internal/service/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,8 @@ func (s *AuthService) Permission(ctx context.Context, req *auth.PermissionReques
defer span.End()
rp = &emptypb.Empty{}
user := jwt.FromServerContext(ctx)
r := biz.CheckPermission{
UserCode: user.Code,
r := &biz.CheckPermission{
UserCode: user.Attrs["code"],
}
if req.Resource != nil {
r.Resource = *req.Resource
Expand All @@ -156,10 +156,12 @@ func (s *AuthService) Permission(ctx context.Context, req *auth.PermissionReques
err = biz.ErrNoPermission(ctx)
return
}
info := s.user.Info(ctx, user.Code)
info := s.user.Info(ctx, user.Attrs["code"])
jwt.AppendToReplyHeader(ctx, jwt.User{
Code: info.Code,
Platform: info.Platform,
Attrs: map[string]string{
"code": info.Code,
"platform": info.Platform,
},
})
return
}
Expand All @@ -171,8 +173,8 @@ func (s *AuthService) Info(ctx context.Context, _ *emptypb.Empty) (rp *auth.Info
rp = &auth.InfoReply{}
rp.Permission = &auth.Permission{}
user := jwt.FromServerContext(ctx)
res := s.user.Info(ctx, user.Code)
permission := s.permission.GetByUserCode(ctx, user.Code)
res := s.user.Info(ctx, user.Attrs["code"])
permission := s.permission.GetByUserCode(ctx, user.Attrs["code"])
copierx.Copy(&rp.Permission, permission)
copierx.Copy(&rp, res)
return
Expand Down

0 comments on commit 007be94

Please sign in to comment.