owncloud / ocis

:atom_symbol: ownCloud Infinite Scale Stack
https://doc.owncloud.com/ocis/next/
Apache License 2.0
1.3k stars 170 forks source link

feat: migration service api #8463

Open DeepDiver1975 opened 5 months ago

DeepDiver1975 commented 5 months ago

Is your feature request related to a problem? Please describe.

As a developer building a migration solution from other cloud services (like ownCloud 10) I'd like to have an api to impersonate any user in ocis. Due to the nature of migrations at no time trough out the migration is running any user interaction is possible.

Describe the solution you'd like

Within OCIS as an admin I can define a migration client which I can turn on/off and delete as I like. The migrations API will basically hold exactly one API call:

POST /../exchangeToken
JSON-Body:
Some user identifier like email or uuid

The response will be a single access token which can be used to interact with ocis apis (mainly file upload and such via webdav as the real migration workhorse will be rclone and any api which will allow an impersonated admin account to create users)

For authentication of this single api call we will use a private/public key mechanism were the private key stays within the migration api client and the public key is shared with the ocis server. E.g some JWT which is signed with the key material.

As soon as the migration is done the client registration can be removed.

butonic commented 5 months ago

You could let ocis mint an access token for a given user using the MachineAuth Provider, eg:

func authContextForUser(client gateway.GatewayAPIClient, userID *userpb.UserId, machineAuthAPIKey string) (context.Context, error) {
    if machineAuthAPIKey == "" {
        return nil, errtypes.NotSupported("machine auth not configured")
    }
    // Get auth
    granteeCtx := ctxpkg.ContextSetUser(context.Background(), &userpb.User{Id: userID})

    authRes, err := client.Authenticate(granteeCtx, &gateway.AuthenticateRequest{
        Type:         "machine",
        ClientId:     "userid:" + userID.OpaqueId,
        ClientSecret: machineAuthAPIKey,
    })
    if err != nil {
        return nil, err
    }
    if authRes.GetStatus().GetCode() != rpc.Code_CODE_OK {
        return nil, errtypes.NewErrtypeFromStatus(authRes.Status)
    }
    granteeCtx = metadata.AppendToOutgoingContext(granteeCtx, ctxpkg.TokenHeader, authRes.Token)
    return granteeCtx, nil
}

The authRes.Token contains the token that you can also use in a HTTP request x-access-token header.

DeepDiver1975 commented 5 months ago

The only issue I see is that once the machineAuthAPIKey is distributed we have no control over the usage time frame and the access scope. Right?

butonic commented 5 months ago

right. the machineAuthAPIKey is currently a shared secret for ocis services.

oCIS also has service accounts, but they cannot act on behalf of other users.

DeepDiver1975 commented 5 months ago

Workaround:

butonic commented 5 months ago

To not leak the machineAuthAPI key you could implement a service that implemnts the impersonation api you described above. It can also be a standalone minimal service without all the boilerplate. Then the admin can run it during the migration and afterwards the code is completely removed.

butonic commented 5 months ago

Discussing with @DeepDiver1975 and @dragotin we think a cli tool that starts and registers a service until the migration is done is more secure than a dedicated service. The proxy can be configured to route the /exchangeToken enpoint to the temporary migration service.

DeepDiver1975 commented 5 months ago

Assigning myself. "Look to my pull request on the first light of the fifth day...."