mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2025-12-31 01:10:20 -06:00
Merge pull request #1962 from owncloud/ocis-1798
This commit is contained in:
@@ -454,7 +454,6 @@ def localApiTests(ctx, storage = 'owncloud', suite = 'apiBugDemonstration', acco
|
||||
'environment' : {
|
||||
'TEST_SERVER_URL': 'https://ocis-server:9200',
|
||||
'OCIS_REVA_DATA_ROOT': '%s' % ('/srv/app/tmp/ocis/owncloud/data/' if storage == 'owncloud' else ''),
|
||||
'DELETE_USER_DATA_CMD': '%s' % ('' if storage == 'owncloud' else 'rm -rf /srv/app/tmp/ocis/storage/users/nodes/root/* /srv/app/tmp/ocis/storage/users/nodes/*-*-*-*'),
|
||||
'SKELETON_DIR': '/srv/app/tmp/testing/data/apiSkeleton',
|
||||
'OCIS_SKELETON_STRATEGY': '%s' % ('copy' if storage == 'owncloud' else 'upload'),
|
||||
'TEST_OCIS':'true',
|
||||
@@ -501,7 +500,6 @@ def coreApiTests(ctx, part_number = 1, number_of_parts = 1, storage = 'owncloud'
|
||||
'environment' : {
|
||||
'TEST_SERVER_URL': 'https://ocis-server:9200',
|
||||
'OCIS_REVA_DATA_ROOT': '%s' % ('/srv/app/tmp/ocis/owncloud/data/' if storage == 'owncloud' else ''),
|
||||
'DELETE_USER_DATA_CMD': '%s' % ('' if storage == 'owncloud' else 'rm -rf /srv/app/tmp/ocis/storage/users/nodes/root/* /srv/app/tmp/ocis/storage/users/nodes/*-*-*-*'),
|
||||
'SKELETON_DIR': '/srv/app/tmp/testing/data/apiSkeleton',
|
||||
'OCIS_SKELETON_STRATEGY': '%s' % ('copy' if storage == 'owncloud' else 'upload'),
|
||||
'TEST_OCIS':'true',
|
||||
|
||||
15
changelog/unreleased/ocs-user-deprovisioning.md
Normal file
15
changelog/unreleased/ocs-user-deprovisioning.md
Normal file
@@ -0,0 +1,15 @@
|
||||
Enhancement: User Deprovisioning for the OCS API
|
||||
|
||||
Use the CS3 API and Reva to deprovision users completely.
|
||||
|
||||
Two new environment variables introduced:
|
||||
```
|
||||
OCS_IDM_ADDRESS
|
||||
OCS_STORAGE_USERS_DRIVER
|
||||
```
|
||||
|
||||
`OCS_IDM_ADDRESS` is also an alias for `OCIS_URL`; allows the OCS service to mint jwt tokens for the authenticated user that will be read by the reva authentication middleware.
|
||||
|
||||
`OCS_STORAGE_USERS_DRIVER` determines how a user is deprovisioned. This kind of behavior is needed since every storage driver deals with deleting differently.
|
||||
|
||||
https://github.com/owncloud/ocis/pull/1962
|
||||
@@ -22,12 +22,14 @@ require (
|
||||
github.com/owncloud/ocis/proxy v0.0.0-20210412105747-9b95e9b1191b
|
||||
github.com/owncloud/ocis/settings v0.0.0-20210413063522-955bd60edf33
|
||||
github.com/owncloud/ocis/store v0.0.0-20210413063522-955bd60edf33
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/prometheus/client_golang v1.10.0
|
||||
github.com/spf13/viper v1.7.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/thejerf/suture/v4 v4.0.0
|
||||
go.opencensus.io v0.23.0
|
||||
google.golang.org/genproto v0.0.0-20210207032614-bba0dbe2a9ea
|
||||
google.golang.org/grpc v1.37.0 // indirect
|
||||
google.golang.org/protobuf v1.26.0
|
||||
)
|
||||
|
||||
|
||||
@@ -45,17 +45,26 @@ type TokenManager struct {
|
||||
JWTSecret string
|
||||
}
|
||||
|
||||
// IdentityManagement keeps track of the OIDC address. This is because Reva requisite of uniqueness for users
|
||||
// is based in the combination of IDP hostname + UserID. For more information see:
|
||||
// https://github.com/cs3org/reva/blob/4fd0229f13fae5bc9684556a82dbbd0eced65ef9/pkg/storage/utils/decomposedfs/node/node.go#L856-L865
|
||||
type IdentityManagement struct {
|
||||
Address string
|
||||
}
|
||||
|
||||
// Config combines all available configuration parts.
|
||||
type Config struct {
|
||||
File string
|
||||
Log Log
|
||||
Debug Debug
|
||||
HTTP HTTP
|
||||
Tracing Tracing
|
||||
TokenManager TokenManager
|
||||
Service Service
|
||||
AccountBackend string
|
||||
RevaAddress string
|
||||
File string
|
||||
Log Log
|
||||
Debug Debug
|
||||
HTTP HTTP
|
||||
Tracing Tracing
|
||||
TokenManager TokenManager
|
||||
Service Service
|
||||
AccountBackend string
|
||||
RevaAddress string
|
||||
StorageUsersDriver string
|
||||
IdentityManagement IdentityManagement
|
||||
|
||||
Context context.Context
|
||||
Supervised bool
|
||||
|
||||
@@ -165,6 +165,20 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag {
|
||||
EnvVars: []string{"OCS_REVA_GATEWAY_ADDR"},
|
||||
Destination: &cfg.RevaAddress,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "idm-address",
|
||||
Value: flags.OverrideDefaultString(cfg.IdentityManagement.Address, "https://localhost:9200"),
|
||||
EnvVars: []string{"OCS_IDM_ADDRESS", "OCIS_URL"},
|
||||
Usage: "keeps track of the IDM Address. Needed because of Reva requisite of uniqueness for users",
|
||||
Destination: &cfg.IdentityManagement.Address,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "users-driver",
|
||||
Value: flags.OverrideDefaultString(cfg.StorageUsersDriver, "ocis"),
|
||||
Usage: "storage driver for users mount: eg. local, eos, owncloud, ocis or s3",
|
||||
EnvVars: []string{"OCS_STORAGE_USERS_DRIVER", "STORAGE_USERS_DRIVER"},
|
||||
Destination: &cfg.StorageUsersDriver,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,18 +10,27 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/asim/go-micro/plugins/client/grpc/v3"
|
||||
merrors "github.com/asim/go-micro/v3/errors"
|
||||
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
||||
cs3 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
revauser "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
"github.com/cs3org/reva/pkg/rgrpc/todo/pool"
|
||||
"github.com/cs3org/reva/pkg/token"
|
||||
"github.com/cs3org/reva/pkg/token/manager/jwt"
|
||||
"github.com/cs3org/reva/pkg/user"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/render"
|
||||
"google.golang.org/genproto/protobuf/field_mask"
|
||||
"google.golang.org/protobuf/types/known/fieldmaskpb"
|
||||
|
||||
merrors "github.com/asim/go-micro/v3/errors"
|
||||
cs3 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
accounts "github.com/owncloud/ocis/accounts/pkg/proto/v0"
|
||||
"github.com/owncloud/ocis/ocs/pkg/service/v0/data"
|
||||
"github.com/owncloud/ocis/ocs/pkg/service/v0/response"
|
||||
storepb "github.com/owncloud/ocis/store/pkg/proto/v0"
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/genproto/protobuf/field_mask"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/protobuf/types/known/fieldmaskpb"
|
||||
)
|
||||
|
||||
// GetSelf returns the currently logged in user
|
||||
@@ -358,6 +367,100 @@ func (o Ocs) DeleteUser(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if o.config.RevaAddress != "" && o.config.StorageUsersDriver != "owncloud" {
|
||||
t, err := o.mintTokenForUser(r.Context(), account)
|
||||
if err != nil {
|
||||
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, errors.Wrap(err, "error minting token").Error())))
|
||||
return
|
||||
}
|
||||
|
||||
ctx := metadata.AppendToOutgoingContext(r.Context(), token.TokenHeader, t)
|
||||
|
||||
gwc, err := pool.GetGatewayServiceClient(o.config.RevaAddress)
|
||||
if err != nil {
|
||||
o.logger.Error().Err(err).Msg("error securing a connection to Reva gateway")
|
||||
}
|
||||
|
||||
homeResp, err := gwc.GetHome(ctx, &provider.GetHomeRequest{})
|
||||
if err != nil {
|
||||
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, errors.Wrap(err, "could not get home").Error())))
|
||||
return
|
||||
}
|
||||
|
||||
if homeResp.Status.Code != rpcv1beta1.Code_CODE_OK {
|
||||
o.logger.Error().
|
||||
Str("stat_status_code", homeResp.Status.Code.String()).
|
||||
Str("stat_message", homeResp.Status.Message).
|
||||
Msg("DeleteUser: could not get user home: get failed")
|
||||
return
|
||||
}
|
||||
|
||||
statResp, err := gwc.Stat(ctx, &provider.StatRequest{
|
||||
Ref: &provider.Reference{
|
||||
Spec: &provider.Reference_Path{
|
||||
Path: homeResp.Path,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, errors.Wrap(err, "could not stat home").Error())))
|
||||
return
|
||||
}
|
||||
|
||||
if statResp.Status.Code != rpcv1beta1.Code_CODE_OK {
|
||||
o.logger.Error().
|
||||
Str("stat_status_code", statResp.Status.Code.String()).
|
||||
Str("stat_message", statResp.Status.Message).
|
||||
Msg("DeleteUser: could not delete user home: stat failed")
|
||||
return
|
||||
}
|
||||
|
||||
delReq := &provider.DeleteRequest{
|
||||
Ref: &provider.Reference{
|
||||
Spec: &provider.Reference_Id{
|
||||
Id: statResp.Info.Id,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
delResp, err := gwc.Delete(ctx, delReq)
|
||||
if err != nil {
|
||||
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, errors.Wrap(err, "could not delete home").Error())))
|
||||
return
|
||||
}
|
||||
|
||||
if delResp.Status.Code != rpcv1beta1.Code_CODE_OK {
|
||||
o.logger.Error().
|
||||
Str("stat_status_code", statResp.Status.Code.String()).
|
||||
Str("stat_message", statResp.Status.Message).
|
||||
Msg("DeleteUser: could not delete user home: delete failed")
|
||||
return
|
||||
}
|
||||
|
||||
req := &gateway.PurgeRecycleRequest{
|
||||
Ref: &provider.Reference{
|
||||
Spec: &provider.Reference_Path{
|
||||
Path: homeResp.Path,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
purgeRecycleResponse, err := gwc.PurgeRecycle(ctx, req)
|
||||
if err != nil {
|
||||
mustNotFail(render.Render(w, r, response.ErrRender(data.MetaServerError.StatusCode, errors.Wrap(err, "could not delete trash").Error())))
|
||||
return
|
||||
}
|
||||
|
||||
if purgeRecycleResponse.Status.Code != rpcv1beta1.Code_CODE_OK {
|
||||
o.logger.Error().
|
||||
Str("stat_status_code", statResp.Status.Code.String()).
|
||||
Str("stat_message", statResp.Status.Message).
|
||||
Msg("DeleteUser: could not delete user trash: delete failed")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
req := accounts.DeleteAccountRequest{
|
||||
Id: account.Id,
|
||||
}
|
||||
@@ -378,6 +481,35 @@ func (o Ocs) DeleteUser(w http.ResponseWriter, r *http.Request) {
|
||||
mustNotFail(render.Render(w, r, response.DataRender(struct{}{})))
|
||||
}
|
||||
|
||||
// TODO(refs) this to ocis-pkg ... we are minting tokens all over the place ... or use a service? ... like reva?
|
||||
func (o Ocs) mintTokenForUser(ctx context.Context, account *accounts.Account) (string, error) {
|
||||
tm, _ := jwt.New(map[string]interface{}{
|
||||
"secret": o.config.TokenManager.JWTSecret,
|
||||
"expires": int64(60),
|
||||
})
|
||||
|
||||
u := &revauser.User{
|
||||
Id: &revauser.UserId{
|
||||
OpaqueId: account.Id,
|
||||
Idp: o.config.IdentityManagement.Address,
|
||||
},
|
||||
Groups: []string{},
|
||||
Opaque: &types.Opaque{
|
||||
Map: map[string]*types.OpaqueEntry{
|
||||
"uid": {
|
||||
Decoder: "plain",
|
||||
Value: []byte(strconv.FormatInt(account.UidNumber, 10)),
|
||||
},
|
||||
"gid": {
|
||||
Decoder: "plain",
|
||||
Value: []byte(strconv.FormatInt(account.GidNumber, 10)),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return tm.MintToken(ctx, u)
|
||||
}
|
||||
|
||||
// EnableUser enables a user
|
||||
func (o Ocs) EnableUser(w http.ResponseWriter, r *http.Request) {
|
||||
userid := chi.URLParam(r, "userid")
|
||||
|
||||
@@ -1539,8 +1539,6 @@ special character username not valid
|
||||
- [apiProvisioning-v2/enableUser.feature:20](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L20)
|
||||
- [apiProvisioning-v2/getUser.feature:34](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getUser.feature#L34)
|
||||
- [apiProvisioning-v2/getUser.feature:35](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getUser.feature#L35)
|
||||
- [apiTrashbin/trashbinFilesFolders.feature:201](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTrashbin/trashbinFilesFolders.feature#L201)
|
||||
- [apiTrashbin/trashbinFilesFolders.feature:202](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTrashbin/trashbinFilesFolders.feature#L202)
|
||||
- [apiTrashbin/trashbinFilesFolders.feature:246](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTrashbin/trashbinFilesFolders.feature#L246)
|
||||
- [apiTrashbin/trashbinFilesFolders.feature:247](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTrashbin/trashbinFilesFolders.feature#L247)
|
||||
- [apiTrashbin/trashbinFilesFolders.feature:248](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTrashbin/trashbinFilesFolders.feature#L248)
|
||||
|
||||
Reference in New Issue
Block a user