Skip to content

Commit

Permalink
Merge pull request #15 from yeqown/feature/refactor-persistence
Browse files Browse the repository at this point in the history
feature/refactor persistence
  • Loading branch information
yeqown committed Apr 1, 2021
2 parents b16e2f5 + 27236d4 commit 351b5e3
Show file tree
Hide file tree
Showing 54 changed files with 1,379 additions and 1,070 deletions.
16 changes: 12 additions & 4 deletions cmd/cassemctl/commands_resctl_user.go
Expand Up @@ -5,6 +5,10 @@ import (

"github.com/yeqown/cassem/internal/authorizer"

"github.com/yeqown/cassem/pkg/hash"

"github.com/yeqown/cassem/internal/persistence"

"github.com/pkg/errors"
"github.com/urfave/cli/v2"
)
Expand Down Expand Up @@ -52,7 +56,7 @@ func addUserCommand() *cli.Command {
Category: "user",
Flags: []cli.Flag{_userMetaDataFlag},
Action: func(ctx *cli.Context) error {
auth, err := getAuthorizer(ctx)
repo, err := getRepository(ctx)
if err != nil {
return err
}
Expand All @@ -62,7 +66,11 @@ func addUserCommand() *cli.Command {
return err
}

_, err = auth.AddUser(md.Account, md.Password, md.Name)
err = repo.CreateUser(&persistence.User{
Account: md.Account,
PasswordWithSalt: hash.WithSalt(md.Password, "cassem"),
Name: md.Name,
})
return err
},
}
Expand All @@ -75,7 +83,7 @@ func resetUserPasswordCommand() *cli.Command {
Category: "user",
Flags: []cli.Flag{_userMetaDataFlag},
Action: func(ctx *cli.Context) error {
auth, err := getAuthorizer(ctx)
repo, err := getRepository(ctx)
if err != nil {
return err
}
Expand All @@ -85,7 +93,7 @@ func resetUserPasswordCommand() *cli.Command {
return err
}

return auth.ResetPassword(md.Account, md.Password)
return repo.ResetPassword(md.Account, hash.WithSalt(md.Password, "cassem"))
},
}
}
Expand Down
Expand Up @@ -7,7 +7,6 @@ import (

"github.com/casbin/casbin/v2"
"github.com/casbin/casbin/v2/model"
gormadapter "github.com/casbin/gorm-adapter/v3"
"github.com/pkg/errors"
"github.com/yeqown/log"
)
Expand All @@ -26,20 +25,40 @@ e = some(where (p.eft == allow))
m = r.sub == p.sub && obj_match(r.obj, p.obj) && act_match(r.act, p.act)
`

type IEnforcer interface {
Enforce(req *EnforceRequest) bool
ListSubjectPolicies(subject string) []Policy
UpdateSubjectPolicies(subject string, policies []Policy) error
}

type IAuthorizer interface {
IEnforcer

Migrate() error
}

type EnforceRequest struct {
Subject string
Object string
Action string

//Namespace string
//Container string
//Pair string
}

// casbinAuthorities implement IAuthorizer based on casbin.ACL model.
type casbinAuthorities struct {
aclEnforcer *casbin.Enforcer
userRepo persistence.UserRepository
repo persistence.Repository
}

func New(c *conf.MySQL) (auth IAuthorizer, err error) {
db, err := mysql.Connect(c)
repo, err := mysql.New(c)
if err != nil {
return nil, errors.Wrap(err, "authorizer.New could not connect to DB")
return nil, errors.Wrap(err, "authorizer.New could not load persistence")
}

// adapter
a, err := gormadapter.NewAdapterByDBUseTableName(db, "cassem", "permission_policy")
a, err := repo.PolicyAdapter()
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -87,7 +106,7 @@ func New(c *conf.MySQL) (auth IAuthorizer, err error) {
})

auth = casbinAuthorities{
userRepo: mysql.NewUserRepository(db),
repo: repo,
aclEnforcer: e,
}

Expand All @@ -97,18 +116,14 @@ func New(c *conf.MySQL) (auth IAuthorizer, err error) {
// Migrate ...
// DONE(@yeqown): migrate to init data, only be called cassemctl.
func (c casbinAuthorities) Migrate() error {
if err := c.userRepo.Migrate(); err != nil {
return errors.Wrap(err, "failed to migrate user table")
}

// DONE(@yeqown) add root account automatically, and add all permissions to root account.
u, err := c.AddUser("admin", "cassem", "admin")
if err != nil {
u := &persistence.User{Account: "admin", PasswordWithSalt: "cassem", Name: "admin"}
if err := c.repo.CreateUser(u); err != nil {
return errors.Wrap(err, "failed to create root account")
}

token := NewToken(int(u.ID))
if err = c.UpdateSubjectPolicies(token.Subject(), allPolicies); err != nil {
if err := c.UpdateSubjectPolicies(token.Subject(), AllPolicies); err != nil {
return errors.Wrap(err, "failed to assign all policy to root account")
}

Expand Down Expand Up @@ -140,31 +155,6 @@ func (c casbinAuthorities) Enforce(req *EnforceRequest) bool {
return allow
}

func (c casbinAuthorities) UpdateSubjectPolicies(subject string, policies []Policy) error {
_, err := c.aclEnforcer.RemoveFilteredPolicy(0, subject)
if err != nil {
return err
}

in := make([][]string, 0, len(policies))
for _, policy := range policies {
if err = validPolicy(subject, policy); err != nil {
log.WithFields(log.Fields{
"subject": subject,
"policy": policy,
}).Warnf("policy invalid, skip")
}
in = append(in, []string{policy.Subject, policy.Object, policy.Action})
}

_, err = c.aclEnforcer.AddPolicies(in)
if err != nil {
return err
}

return nil
}

func (c casbinAuthorities) ListSubjectPolicies(subject string) []Policy {
out := c.aclEnforcer.GetFilteredPolicy(0, subject)

Expand All @@ -188,3 +178,28 @@ func (c casbinAuthorities) ListSubjectPolicies(subject string) []Policy {

return policies
}

func (c casbinAuthorities) UpdateSubjectPolicies(subject string, policies []Policy) error {
_, err := c.aclEnforcer.RemoveFilteredPolicy(0, subject)
if err != nil {
return err
}

in := make([][]string, 0, len(policies))
for _, policy := range policies {
if err = ValidPolicy(subject, policy); err != nil {
log.WithFields(log.Fields{
"subject": subject,
"policy": policy,
}).Warnf("policy invalid, skip")
}
in = append(in, []string{policy.Subject, policy.Object, policy.Action})
}

_, err = c.aclEnforcer.AddPolicies(in)
if err != nil {
return err
}

return nil
}
Expand Up @@ -86,19 +86,20 @@ func Test_IAuthorizer_AddPolicy(t *testing.T) {
assert.Equal(t, false, allow)
}

func Test_IAuthorizer_LoginAndSession(t *testing.T) {
a, err := authorizer.New(_conf)
require.Nil(t, err)

tokenString, err := a.Login("root", "123456")
require.Nil(t, err)
t.Log(tokenString)

token, err := a.Session(tokenString)
require.Nil(t, err)

assert.NotEmpty(t, token.UserId)
}
//
//func Test_IAuthorizer_LoginAndSession(t *testing.T) {
// a, err := authorizer.New(_conf)
// require.Nil(t, err)
//
// _, tokenString, err := a.Login("root", "123456")
// require.Nil(t, err)
// t.Log(tokenString)
//
// token, err := authorizer.Session(tokenString)
// require.Nil(t, err)
//
// assert.NotEmpty(t, token.UserId)
//}

func Test_IAuthorizer_ListPolicy(t *testing.T) {
a, err := authorizer.New(_conf)
Expand Down
Expand Up @@ -2,9 +2,9 @@ package authorizer

import (
"fmt"
"strconv"

"github.com/yeqown/cassem/internal/persistence"
"github.com/yeqown/cassem/pkg/hash"

"github.com/dgrijalva/jwt-go"
"github.com/pkg/errors"
Expand All @@ -15,32 +15,20 @@ var (
_SECRET_ = []byte("cassem")
)

func (c casbinAuthorities) AddUser(account, password, name string) (*persistence.UserDO, error) {
u := &persistence.UserDO{
Account: account,
PasswordWithSalt: hash.WithSalt(password, "cassem"),
Name: name,
}

return u, c.userRepo.Create(u)
// Token is the bridge between authorizer and HTTP API.
type Token struct {
UserId int
}

func (c casbinAuthorities) Login(account, password string) (*persistence.UserDO, string, error) {
u, err := c.userRepo.QueryUser(account)
if err != nil {
return nil, "", err
}

if u.PasswordWithSalt != hash.WithSalt(password, "cassem") {
return nil, "", errors.New("account and password could not match")
}
func NewToken(uid int) *Token {
return &Token{UserId: uid}
}

// DONE(@yeqown): generate jwt token
token, err := genToken(u)
return u, token, err
func (t Token) Subject() string {
return "uid:" + strconv.Itoa(t.UserId)
}

func (c casbinAuthorities) Session(tokenString string) (*Token, error) {
func Session(tokenString string) (*Token, error) {
uid, err := parseToken(tokenString)
if err != nil {
return nil, err
Expand All @@ -50,7 +38,7 @@ func (c casbinAuthorities) Session(tokenString string) (*Token, error) {
}

// DONE(@yeqown): extract secret from code to global.
func genToken(u *persistence.UserDO) (string, error) {
func GenToken(u *persistence.User) (string, error) {
atClaims := jwt.MapClaims{}
atClaims["authorized"] = true
atClaims["user_id"] = u.ID
Expand Down Expand Up @@ -90,15 +78,3 @@ func parseToken(tokenString string) (int, error) {

return int(uid), nil
}

func (c casbinAuthorities) ResetPassword(account, password string) error {
return c.userRepo.ResetPassword(account, hash.WithSalt(password, "cassem"))
}

func (c casbinAuthorities) PagingUsers(limit, offset int, accountPattern string) ([]*persistence.UserDO, int, error) {
return c.userRepo.PagingUsers(&persistence.PagingUsersFilter{
Limit: limit,
Offset: offset,
AccountPattern: accountPattern,
})
}
54 changes: 2 additions & 52 deletions internal/authorizer/authorizer.go → internal/authorizer/types.go
@@ -1,10 +1,6 @@
package authorizer

import (
"strconv"

"github.com/yeqown/cassem/internal/persistence"

"github.com/pkg/errors"
)

Expand All @@ -24,7 +20,7 @@ const (
)

var (
allPolicies = []Policy{
AllPolicies = []Policy{
{Subject: "", Object: OBJ_NAMESPACE, Action: ACTION_ANY},
{Subject: "", Object: OBJ_CONTAINER, Action: ACTION_ANY},
{Subject: "", Object: OBJ_PAIR, Action: ACTION_ANY},
Expand All @@ -39,31 +35,14 @@ var (
}
)

type EnforceRequest struct {
Subject string
Object string
Action string

//Namespace string
//Container string
//Pair string
}

// IAuthorizer
type IAuthorizer interface {
IAuthorizeManager

Enforce(req *EnforceRequest) bool
}

// Policy contains whole data what describes a ACL rule.
type Policy struct {
Subject string
Object string
Action string
}

func validPolicy(subject string, p Policy) (err error) {
func ValidPolicy(subject string, p Policy) (err error) {
var errmsg string
defer func() {
if errmsg != "" {
Expand All @@ -84,32 +63,3 @@ func validPolicy(subject string, p Policy) (err error) {

return
}

// Token is the bridge between authorizer and HTTP API.
type Token struct {
UserId int
}

func NewToken(uid int) *Token {
return &Token{UserId: uid}
}

func (t Token) Subject() string {
return "uid:" + strconv.Itoa(t.UserId)
}

// IAuthorizeManager manages user, roles.
type IAuthorizeManager interface {
Migrate() error

// user permissions manage API
ListSubjectPolicies(subject string) []Policy
UpdateSubjectPolicies(subject string, policies []Policy) error

// user and session manage API
AddUser(account, password, name string) (*persistence.UserDO, error)
Login(account, password string) (*persistence.UserDO, string, error)
Session(tokenString string) (*Token, error)
ResetPassword(account, password string) error
PagingUsers(limit, offset int, accountPattern string) ([]*persistence.UserDO, int, error)
}

0 comments on commit 351b5e3

Please sign in to comment.