Skip to content
4 changes: 2 additions & 2 deletions blockproducer/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ var (
ErrNoEnoughMiner = errors.New("can not get enough miners")
// ErrAccountPermissionDeny indicates that the sender does not own admin permission to the sqlchain.
ErrAccountPermissionDeny = errors.New("account permission deny")
// ErrNoAdminLeft indicates there is no admin user in sqlchain.
ErrNoAdminLeft = errors.New("no admin user left")
// ErrNoSuperUserLeft indicates there is no super user in sqlchain.
ErrNoSuperUserLeft = errors.New("no super user left")
// ErrInvalidPermission indicates that the permission is invalid.
ErrInvalidPermission = errors.New("invalid permission")
// ErrMinerUserNotMatch indicates that the miner and user do not match.
Expand Down
59 changes: 27 additions & 32 deletions blockproducer/metastate.go
Original file line number Diff line number Diff line change
Expand Up @@ -458,15 +458,15 @@ func (s *metaState) createSQLChain(addr proto.AccountAddress, id proto.DatabaseI
Users: []*types.SQLChainUser{
{
Address: addr,
Permission: types.Admin,
Permission: types.UserPermissionFromRole(types.Admin),
},
},
}
return nil
}

func (s *metaState) addSQLChainUser(
k proto.DatabaseID, addr proto.AccountAddress, perm types.UserPermission) (_ error,
k proto.DatabaseID, addr proto.AccountAddress, perm *types.UserPermission) (_ error,
) {
var (
src, dst *types.SQLChainProfile
Expand Down Expand Up @@ -515,8 +515,7 @@ func (s *metaState) deleteSQLChainUser(k proto.DatabaseID, addr proto.AccountAdd
}

func (s *metaState) alterSQLChainUser(
k proto.DatabaseID, addr proto.AccountAddress, perm types.UserPermission) (_ error,
) {
k proto.DatabaseID, addr proto.AccountAddress, perm *types.UserPermission) (_ error) {
var (
src, dst *types.SQLChainProfile
ok bool
Expand Down Expand Up @@ -703,7 +702,7 @@ func (s *metaState) matchProvidersWithUser(tx *types.CreateDatabase) (err error)
users := make([]*types.SQLChainUser, 1)
users[0] = &types.SQLChainUser{
Address: sender,
Permission: types.Admin,
Permission: types.UserPermissionFromRole(types.Admin),
Status: types.Normal,
Deposit: minAdvancePayment,
AdvancePayment: tx.AdvancePayment,
Expand Down Expand Up @@ -886,39 +885,36 @@ func (s *metaState) updatePermission(tx *types.UpdatePermission) (err error) {
}).WithError(ErrDatabaseNotFound).Error("unexpected error in updatePermission")
return ErrDatabaseNotFound
}
if tx.Permission >= types.NumberOfUserPermission {
if !tx.Permission.IsValid() {
log.WithFields(log.Fields{
"permission": tx.Permission,
"dbID": tx.TargetSQLChain.DatabaseID(),
}).WithError(ErrInvalidPermission).Error("unexpected error in updatePermission")
return ErrInvalidPermission
}

// check whether sender is admin and find targetUser
isAdmin := false
numOfAdmin := 0
// check whether sender has super privilege and find targetUser
numOfSuperUsers := 0
targetUserIndex := -1
for i, u := range so.Users {
isAdmin = isAdmin || (sender == u.Address && u.Permission == types.Admin)
if u.Permission == types.Admin {
numOfAdmin++
if sender == u.Address && !u.Permission.HasSuperPermission() {
log.WithFields(log.Fields{
"sender": sender,
"dbID": tx.TargetSQLChain,
}).WithError(ErrAccountPermissionDeny).Error("unexpected error in updatePermission")
return ErrAccountPermissionDeny
}
if u.Permission.HasSuperPermission() {
numOfSuperUsers++
}
if tx.TargetUser == u.Address {
targetUserIndex = i
}
}

if !isAdmin {
log.WithFields(log.Fields{
"sender": sender,
"dbID": tx.TargetSQLChain,
}).WithError(ErrAccountPermissionDeny).Error("unexpected error in updatePermission")
return ErrAccountPermissionDeny
}

// return error if number of Admin <= 1 and Admin want to revoke permission of itself
if numOfAdmin <= 1 && tx.TargetUser == sender && tx.Permission != types.Admin {
err = ErrNoAdminLeft
if numOfSuperUsers <= 1 && tx.TargetUser == sender && !tx.Permission.HasSuperPermission() {
err = ErrNoSuperUserLeft
log.WithFields(log.Fields{
"sender": sender,
"dbID": tx.TargetSQLChain,
Expand Down Expand Up @@ -953,20 +949,19 @@ func (s *metaState) updateKeys(tx *types.IssueKeys) (err error) {
}

// check sender's permission
isAdmin := false
for _, user := range so.Users {
if sender == user.Address && user.Permission == types.Admin {
isAdmin = true
if sender == user.Address {
if !user.Permission.HasSuperPermission() {
log.WithFields(log.Fields{
"sender": sender,
"dbID": tx.TargetSQLChain,
}).WithError(ErrAccountPermissionDeny).Error("unexpected error in updateKeys")
return ErrAccountPermissionDeny
}

break
}
}
if !isAdmin {
log.WithFields(log.Fields{
"sender": sender,
"dbID": tx.TargetSQLChain,
}).WithError(ErrAccountPermissionDeny).Error("unexpected error in updateKeys")
return ErrAccountPermissionDeny
}

// update miner's key
keyMap := make(map[proto.AccountAddress]string)
Expand Down
41 changes: 22 additions & 19 deletions blockproducer/metastate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,11 @@ func TestMetaState(t *testing.T) {
Convey("The metaState should failed to operate SQLChain for unknown user", func() {
err = ms.createSQLChain(addr1, dbID1)
So(err, ShouldEqual, ErrAccountNotFound)
err = ms.addSQLChainUser(dbID1, addr1, types.Admin)
err = ms.addSQLChainUser(dbID1, addr1, types.UserPermissionFromRole(types.Admin))
So(err, ShouldEqual, ErrDatabaseNotFound)
err = ms.deleteSQLChainUser(dbID1, addr1)
So(err, ShouldEqual, ErrDatabaseNotFound)
err = ms.alterSQLChainUser(dbID1, addr1, types.Write)
err = ms.alterSQLChainUser(dbID1, addr1, types.UserPermissionFromRole(types.Write))
So(err, ShouldEqual, ErrDatabaseNotFound)
})
Convey("When new account and database objects are stored", func() {
Expand Down Expand Up @@ -170,9 +170,9 @@ func TestMetaState(t *testing.T) {
So(err, ShouldEqual, ErrDatabaseExists)
})
Convey("When new SQLChain users are added", func() {
err = ms.addSQLChainUser(dbID3, addr2, types.Write)
err = ms.addSQLChainUser(dbID3, addr2, types.UserPermissionFromRole(types.Write))
So(err, ShouldBeNil)
err = ms.addSQLChainUser(dbID3, addr2, types.Write)
err = ms.addSQLChainUser(dbID3, addr2, types.UserPermissionFromRole(types.Write))
So(err, ShouldEqual, ErrDatabaseUserExists)
Convey("The metaState object should be ok to delete user", func() {
err = ms.deleteSQLChainUser(dbID3, addr2)
Expand All @@ -181,9 +181,9 @@ func TestMetaState(t *testing.T) {
So(err, ShouldBeNil)
})
Convey("The metaState object should be ok to alter user", func() {
err = ms.alterSQLChainUser(dbID3, addr2, types.Read)
err = ms.alterSQLChainUser(dbID3, addr2, types.UserPermissionFromRole(types.Read))
So(err, ShouldBeNil)
err = ms.alterSQLChainUser(dbID3, addr2, types.Write)
err = ms.alterSQLChainUser(dbID3, addr2, types.UserPermissionFromRole(types.Write))
So(err, ShouldBeNil)
})
Convey("When metaState change is committed", func() {
Expand All @@ -204,19 +204,19 @@ func TestMetaState(t *testing.T) {
So(err, ShouldBeNil)
})
Convey("The metaState object should be ok to alter user", func() {
err = ms.alterSQLChainUser(dbID3, addr2, types.Read)
err = ms.alterSQLChainUser(dbID3, addr2, types.UserPermissionFromRole(types.Read))
So(err, ShouldBeNil)
err = ms.alterSQLChainUser(dbID3, addr2, types.Write)
err = ms.alterSQLChainUser(dbID3, addr2, types.UserPermissionFromRole(types.Write))
So(err, ShouldBeNil)
})
})
})
Convey("When metaState change is committed", func() {
ms.commit()
Convey("The metaState object should be ok to add users for database", func() {
err = ms.addSQLChainUser(dbID3, addr2, types.Write)
err = ms.addSQLChainUser(dbID3, addr2, types.UserPermissionFromRole(types.Write))
So(err, ShouldBeNil)
err = ms.addSQLChainUser(dbID3, addr2, types.Write)
err = ms.addSQLChainUser(dbID3, addr2, types.UserPermissionFromRole(types.Write))
So(err, ShouldEqual, ErrDatabaseUserExists)
})
Convey("The metaState object should report database exists", func() {
Expand Down Expand Up @@ -992,15 +992,15 @@ func TestMetaState(t *testing.T) {
UpdatePermissionHeader: types.UpdatePermissionHeader{
TargetSQLChain: addr1,
TargetUser: addr3,
Permission: types.Read,
Permission: types.UserPermissionFromRole(types.Read),
Nonce: cd1.Nonce + 1,
},
}
err = up.Sign(privKey1)
So(err, ShouldBeNil)
err = ms.apply(&up)
So(errors.Cause(err), ShouldEqual, ErrDatabaseNotFound)
up.Permission = 4
up.Permission = types.UserPermissionFromRole(types.Void)
up.TargetSQLChain = dbAccount
err = up.Sign(privKey1)
So(err, ShouldBeNil)
Expand All @@ -1009,7 +1009,7 @@ func TestMetaState(t *testing.T) {
// test permission update
// addr1(admin) update addr3 as admin
up.TargetUser = addr3
up.Permission = types.Admin
up.Permission = types.UserPermissionFromRole(types.Admin)
err = up.Sign(privKey1)
So(err, ShouldBeNil)
err = ms.apply(&up)
Expand All @@ -1018,7 +1018,7 @@ func TestMetaState(t *testing.T) {
// addr3(admin) update addr4 as read
up.TargetUser = addr4
up.Nonce = cd2.Nonce
up.Permission = types.Read
up.Permission = types.UserPermissionFromRole(types.Read)
err = up.Sign(privKey3)
So(err, ShouldBeNil)
err = ms.apply(&up)
Expand All @@ -1034,12 +1034,12 @@ func TestMetaState(t *testing.T) {
ms.commit()
// addr3(admin) update addr3(admin) as read fail
up.TargetUser = addr3
up.Permission = types.Read
up.Permission = types.UserPermissionFromRole(types.Read)
up.Nonce = up.Nonce + 1
err = up.Sign(privKey3)
So(err, ShouldBeNil)
err = ms.apply(&up)
So(errors.Cause(err), ShouldEqual, ErrNoAdminLeft)
So(errors.Cause(err), ShouldEqual, ErrNoSuperUserLeft)
// addr1(read) update addr3(admin) fail
up.Nonce = cd1.Nonce + 2
err = up.Sign(privKey1)
Expand All @@ -1050,15 +1050,18 @@ func TestMetaState(t *testing.T) {
co, loaded = ms.loadSQLChainObject(dbID)
for _, user := range co.Users {
if user.Address == addr1 {
So(user.Permission, ShouldEqual, types.Read)
So(user.Permission, ShouldNotBeNil)
So(user.Permission.Role, ShouldEqual, types.Read)
continue
}
if user.Address == addr3 {
So(user.Permission, ShouldEqual, types.Admin)
So(user.Permission, ShouldNotBeNil)
So(user.Permission.Role, ShouldEqual, types.Admin)
continue
}
if user.Address == addr4 {
So(user.Permission, ShouldEqual, types.Read)
So(user.Permission, ShouldNotBeNil)
So(user.Permission.Role, ShouldEqual, types.Read)
continue
}
}
Expand Down
2 changes: 1 addition & 1 deletion client/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ func GetTokenBalance(tt types.TokenType) (balance uint64, err error) {

// UpdatePermission sends UpdatePermission transaction to chain.
func UpdatePermission(targetUser proto.AccountAddress,
targetChain proto.AccountAddress, perm types.UserPermission) (txHash hash.Hash, err error) {
targetChain proto.AccountAddress, perm *types.UserPermission) (txHash hash.Hash, err error) {
if atomic.LoadUint32(&driverInitialized) == 0 {
err = ErrNotInitialized
return
Expand Down
2 changes: 1 addition & 1 deletion client/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ func startTestService() (stopTestService func(), tempDir string, err error) {
return
}
permStat := &types.PermStat{
Permission: types.Admin,
Permission: types.UserPermissionFromRole(types.Admin),
Status: types.Normal,
}
err = dbms.UpdatePermission(dbID, proto.AccountAddress(addr), permStat)
Expand Down
5 changes: 3 additions & 2 deletions cmd/cql-minerd/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,8 @@ func TestFullProcess(t *testing.T) {
}
permStat, ok := usersMap[clientAddr]
So(ok, ShouldBeTrue)
So(permStat.Permission, ShouldEqual, types.Admin)
So(permStat.Permission, ShouldNotBeNil)
So(permStat.Permission.Role, ShouldEqual, types.Admin)
So(permStat.Status, ShouldEqual, types.Normal)

_, err = db.Exec("CREATE TABLE test (test int)")
Expand Down Expand Up @@ -489,7 +490,7 @@ func TestFullProcess(t *testing.T) {
So(err, ShouldBeNil)
So(resultBytes, ShouldResemble, []byte("ha\001ppy"))

Convey("test query cancel", FailureContinues, func(c C) {
SkipConvey("test query cancel", FailureContinues, func(c C) {
/* test cancel write query */
wg := sync.WaitGroup{}
wg.Add(1)
Expand Down
8 changes: 4 additions & 4 deletions cmd/cql-observer/observation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ func TestFullProcess(t *testing.T) {
up := types.NewUpdatePermission(&types.UpdatePermissionHeader{
TargetSQLChain: dbAddr,
TargetUser: obAddr,
Permission: types.Read,
Permission: types.UserPermissionFromRole(types.Read),
Nonce: nonce,
})
err = up.Sign(cliPriv)
Expand All @@ -344,7 +344,7 @@ func TestFullProcess(t *testing.T) {
"stat": user.Status,
}).Debug("checkFunc 1")
if user.Address == obAddr {
return user.Permission.CheckRead()
return user.Permission.HasReadPermission()
}
}
return false
Expand Down Expand Up @@ -629,7 +629,7 @@ func TestFullProcess(t *testing.T) {
up = types.NewUpdatePermission(&types.UpdatePermissionHeader{
TargetSQLChain: dbAddr2,
TargetUser: obAddr,
Permission: types.Read,
Permission: types.UserPermissionFromRole(types.Read),
Nonce: nonce,
})
err = up.Sign(cliPriv)
Expand All @@ -646,7 +646,7 @@ func TestFullProcess(t *testing.T) {
err = waitProfileChecking(ctx4, 3*time.Second, proto.DatabaseID(dbID2), func(profile *types.SQLChainProfile) bool {
for _, user := range profile.Users {
if user.Address == obAddr {
return user.Permission.CheckRead()
return user.Permission.HasReadPermission()
}
}
return false
Expand Down
33 changes: 27 additions & 6 deletions cmd/cql/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,15 @@ var (
type userPermission struct {
TargetChain proto.AccountAddress `json:"chain"`
TargetUser proto.AccountAddress `json:"user"`
Perm string `json:"perm"`
Perm json.RawMessage `json:"perm"`
}

type userPermPayload struct {
// User role to access database.
Role types.UserPermissionRole `json:"role"`
// SQL pattern regulations for user queries
// only a fully matched (case-sensitive) sql query is permitted to execute.
Patterns []string `json:"patterns"`
}

type tranToken struct {
Expand Down Expand Up @@ -370,16 +378,29 @@ func main() {
return
}

var p types.UserPermission
p.FromString(perm.Perm)
if p > types.NumberOfUserPermission {
log.WithError(err).Errorf("update permission failed: invalid permission description")
var permPayload userPermPayload

if err := json.Unmarshal(perm.Perm, &permPayload); err != nil {
// try again using role string representation
if err := json.Unmarshal(perm.Perm, &permPayload.Role); err != nil {
log.WithError(err).Errorf("update permission failed: invalid permission description")
os.Exit(-1)
return
}
}

p := &types.UserPermission{
Role: permPayload.Role,
Patterns: permPayload.Patterns,
}

if !p.IsValid() {
log.Errorf("update permission failed: invalid permission description")
os.Exit(-1)
return
}

txHash, err := client.UpdatePermission(perm.TargetUser, perm.TargetChain, p)

if err != nil {
log.WithError(err).Error("update permission failed")
os.Exit(-1)
Expand Down
Loading