Skip to content

Commit

Permalink
able to change password
Browse files Browse the repository at this point in the history
  • Loading branch information
tianxiaoliang committed Jun 28, 2020
1 parent bd88a19 commit b4f32d9
Show file tree
Hide file tree
Showing 11 changed files with 245 additions and 167 deletions.
37 changes: 28 additions & 9 deletions docs/user-guides/rbac.md
Expand Up @@ -13,23 +13,27 @@ openssl rsa -in private.key -pubout -out public.key
```

2.edit app.conf

can revoke private.key after each cluster restart,
```ini
rbac_enabled = true
rbac_rsa_pub_key_file = ./public.key
rbac_rsa_public_key_file = ./public.key
rbac_rsa_private_key_file = ./private.key
```
3.root account
before you start server, you need to set env to set your root account password.

before you start server, you need to set env to set your root account.
can revoke private.key after each cluster restart,can not revoke root name and password
```sh
export SC_INIT_ROOT_USERNAME=root
export SC_INIT_ROOT_PASSWORD=rootpwd
export SC_INIT_PRIVATE_KEY=`cat private.key`
```
at the first time service center cluster init, it will use this env to setup rbac module.
at the first time service center cluster init, it will use this env to setup rbac module. you can not revoke password after cluster start

the root account name is "root"

To securely distribute your root account and private key,
you can use kubernetes [secret](https://kubernetes.io/zh/docs/tasks/inject-data-application/distribute-credentials-secure/)
### Generate a token
token is the only credential to access rest API, before you access any API, you need to get a token
```shell script
curl -X POST \
http://127.0.0.1:30100/v4/token \
Expand All @@ -42,12 +46,27 @@ will return a token, token will expired after 30m
```

### Authentication
in each request you must add token to http header
in each request you must add token to http header:
```
Authorization: Bear {token}

```
for example:
```shell script
curl -X GET \
'http://127.0.0.1:30100/v4/default/registry/microservices/{service-id}/instances' \
-H 'Authorization: Bear eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1OTI4OTQ1NTEsInVzZXIiOiJyb290In0.FfLOSvVmHT9qCZSe_6iPf4gNjbXLwCrkXxKHsdJoQ8w'
```
```

### Change password
You must supply current password and token to update to new password
curl -X PUT \
http://127.0.0.1:30100/v4/account-password \
-H 'Authorization: Bear eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50Ijoicm9vdCIsImV4cCI6MTU5MzMyOTE3OSwicm9sZSI6IiJ9.OR_uruuLds1wz10_J4gDEA-L9Ma_1RrHiKEA6CS-Nilv6hHB5KyhZ9_4qqf_c0iia4uKryAGHKsXUvrjOE51tz4QCXlgCmddrkYuLQsnDezXhV3TIqzdl4R_cy8h2cZo8O_b_q7eU2Iemd6x7BJE49SLgNiP5LTXCVct5Qm_GiXYTaM4dbHIJ01V-EPmNQuBr1vKdfNa8cqWtASSp9IEkFx1YpzhFacQgmfoiSGHvxQYZldQXuAh60ZXLBDexGu6jGnG39MqVNRysvHTpZRqxZWBhmEn5DeXpgKu-zlakJMjeEma4zcN-H0MumE-nMlBT5kjKWVr1DOdtOyJI6i786ZpS0wWHV4VOxpSursoKsW_XuTZCMM8LTBgdy5icCuHUXvvWXYJxPks9Pq3DcFjPlY3IuXyfokEWxGvrAF6jzglgSrNTiRkoNBKVktEapDyrpyWfktp22mhvWF6GuNoUzztxFPJblH-TXdudzWeqx-gV1lsRPSMsW8-oq6pxJfeb-b0PNM8vAIbwvv8an4T5iNMBZMz7J9NbpVCaj5eLcgfUXktyb8eWSfANhYMxY9kQN9dHZlkASAW-sjehi-rBXYJ8aCL4EbLzrYlmFWoN0z25dxvAxmWaPRQED3METYyZHvV_G4DSQf0cB2Oer_YdoRa6HWmxnTlz0HwPEq55PM' \
-d '{
"currentPassword":"rootpwd",
"password":"123"
}'
### Roles
currently, you can not custom and manage any role and role policy. there is only 1 build in roles
- admin: able to do anything, including manage account, even change other account password
- service: able to call most of API except account management.
12 changes: 9 additions & 3 deletions pkg/model/account.go
Expand Up @@ -17,11 +17,17 @@

package model

const (
RoleAdmin = "admin"
RoleAuditor = "auditor"
)

type Account struct {
Name string `json:"name,omitempty"`
Password string `json:"password,omitempty"`
Name string `json:"name,omitempty"`
Password string `json:"password,omitempty"`
Role string `json:"role,omitempty"`
CurrentPassword string `json:"currentPassword,omitempty"`
}

type Token struct {
TokenStr string `json:"token,omitempty"`
}
20 changes: 13 additions & 7 deletions server/handler/auth/auth.go
Expand Up @@ -17,6 +17,7 @@
package auth

import (
"context"
"github.com/apache/servicecomb-service-center/pkg/chain"
"github.com/apache/servicecomb-service-center/pkg/log"
"github.com/apache/servicecomb-service-center/pkg/rest"
Expand Down Expand Up @@ -63,15 +64,20 @@ func (h *Handler) Handle(i *chain.Invocation) {
}
to := s[1]
//TODO rbac
_, err := authr.Authenticate(i.Context(), to)
if err == nil {
log.Info("user access")
i.Next()
claims, err := authr.Authenticate(i.Context(), to)
if err != nil {
log.Errorf(err, "authenticate request failed, %s %s", req.Method, req.RequestURI)
controller.WriteError(w, scerr.ErrUnauthorized, err.Error())
i.Fail(nil)
return
}
log.Errorf(err, "authenticate request failed, %s %s", req.Method, req.RequestURI)
controller.WriteError(w, scerr.ErrUnauthorized, err.Error())
i.Fail(nil)
log.Info("user access")
req2 := req.WithContext(context.WithValue(req.Context(), "accountInfo", claims))

*req = *req2
i.Next()
return

}
func mustAuth(req *http.Request) bool {
if strings.Contains(req.URL.Path, "/v4/token") {
Expand Down
53 changes: 52 additions & 1 deletion server/rest/controller/v4/auth_resource.go
Expand Up @@ -19,12 +19,14 @@ package v4
import (
"context"
"encoding/json"
"errors"
"github.com/apache/servicecomb-service-center/pkg/log"
"github.com/apache/servicecomb-service-center/pkg/model"
"github.com/apache/servicecomb-service-center/pkg/rest"
"github.com/apache/servicecomb-service-center/server/rest/controller"
"github.com/apache/servicecomb-service-center/server/scerror"
"github.com/apache/servicecomb-service-center/server/service/rbac"
"github.com/apache/servicecomb-service-center/server/service/util"
"github.com/go-chassis/go-chassis/security/authr"
"io/ioutil"
"net/http"
Expand All @@ -37,9 +39,58 @@ type AuthResource struct {
func (r *AuthResource) URLPatterns() []rest.Route {
return []rest.Route{
{http.MethodPost, "/v4/token", r.Login},
{http.MethodPut, "/v4/account-password", r.ChangePassword},
}
}
func (r *AuthResource) ChangePassword(w http.ResponseWriter, req *http.Request) {
body, err := ioutil.ReadAll(req.Body)
if err != nil {
log.Error("read body err", err)
controller.WriteError(w, scerror.ErrInternal, err.Error())
return
}
a := &model.Account{}
if err = json.Unmarshal(body, a); err != nil {
log.Error("json err", err)
controller.WriteError(w, scerror.ErrInvalidParams, err.Error())
return
}
if a.Password == "" {
controller.WriteError(w, scerror.ErrInvalidParams, "new password is empty")
return
}
claims := req.Context().Value("accountInfo")
m, ok := claims.(map[string]interface{})
if !ok {
log.Error("claims convert failed", errors.New(util.ErrMsgConvert))
controller.WriteError(w, scerror.ErrInvalidParams, util.ErrMsgConvert)
return
}
accountNameI := m[rbac.ClaimsUser]
changer, ok := accountNameI.(string)
if !ok {
log.Error("claims convert failed", errors.New(util.ErrMsgConvert))
controller.WriteError(w, scerror.ErrInternal, util.ErrMsgConvert)
return
}
roleI := m[rbac.ClaimsRole]
role, ok := roleI.(string)
if !ok {
log.Error("claims convert failed", errors.New(util.ErrMsgConvert))
controller.WriteError(w, scerror.ErrInternal, util.ErrMsgConvert)
return
}
if role == "" {
controller.WriteError(w, scerror.ErrInvalidParams, "role is empty")
return
}
err = rbac.ChangePassword(context.TODO(), role, changer, a)
if err != nil {
log.Error("change password failed", err)
controller.WriteError(w, scerror.ErrInternal, err.Error())
return
}
}

func (r *AuthResource) Login(w http.ResponseWriter, req *http.Request) {
body, err := ioutil.ReadAll(req.Body)
if err != nil {
Expand Down
8 changes: 5 additions & 3 deletions server/service/rbac/authr_plugin.go
Expand Up @@ -31,7 +31,8 @@ import (
var ErrUnauthorized = errors.New("wrong user name or password")

const (
ClaimsUser = "user"
ClaimsUser = "account"
ClaimsRole = "role"
)

//EmbeddedAuthenticator is sc default auth plugin, RBAC data is persisted in etcd
Expand All @@ -57,12 +58,13 @@ func (a *EmbeddedAuthenticator) Login(ctx context.Context, user string, password
return "", err
}
if user == account.Name && password == account.Password {
secret, err := GetPrivateKey(ctx)
secret, err := GetPrivateKey()
if err != nil {
return "", err
}
tokenStr, err := token.Sign(map[string]interface{}{
ClaimsUser: user, //TODO more claims for RBAC, for example role name
ClaimsUser: user,
ClaimsRole: account.Role, //TODO more claims for RBAC, for example rule config
},
secret,
token.WithExpTime("30m"),
Expand Down
28 changes: 28 additions & 0 deletions server/service/rbac/dao/account_dao.go
Expand Up @@ -30,6 +30,7 @@ import (
)

var ErrDuplicated = errors.New("account is duplicated")
var ErrCanNotEdit = errors.New("account can not be edited")

//CreateAccount save 2 kv
//1. account info
Expand Down Expand Up @@ -86,3 +87,30 @@ func AccountExist(ctx context.Context, name string) (bool, error) {
}
return exist, nil
}

//CreateAccount save 2 kv
//1. account info
func EditAccount(ctx context.Context, a *model.Account) error {
key := core.GenerateAccountKey(a.Name)
exist, err := kv.Exist(ctx, key)
if err != nil {
log.Errorf(err, "can not edit account info")
return err
}
if !exist {
return ErrCanNotEdit
}

value, err := json.Marshal(a)
if err != nil {
log.Errorf(err, "account info is invalid")
return err
}
err = kv.PutBytes(ctx, key, value)
if err != nil {
log.Errorf(err, "can not edit account info")
return err
}

return nil
}
53 changes: 0 additions & 53 deletions server/service/rbac/dao/secret_dao.go

This file was deleted.

36 changes: 0 additions & 36 deletions server/service/rbac/dao/secret_dao_test.go

This file was deleted.

0 comments on commit b4f32d9

Please sign in to comment.