diff --git a/changelog/unreleased/enhancement-graph-beta-sharedWithMe-api b/changelog/unreleased/enhancement-graph-beta-sharedWithMe-api new file mode 100644 index 00000000000..9579f2d4420 --- /dev/null +++ b/changelog/unreleased/enhancement-graph-beta-sharedWithMe-api @@ -0,0 +1,7 @@ +Enhancement: Add graph beta sharedWithMe API + +Add graph beta api implementation for the sharedWithMe endpoint. +The implementation is a logical replication of the existing OCS API. + +https://github.com/owncloud/ocis/pull/7633 +https://github.com/owncloud/ocis/issues/7436 diff --git a/go.mod b/go.mod index ae5cb159b93..a78ba21e9e1 100644 --- a/go.mod +++ b/go.mod @@ -304,6 +304,9 @@ require ( github.com/stretchr/objx v0.5.0 // indirect github.com/studio-b12/gowebdav v0.0.0-20221015232716-17255f2e7423 // indirect github.com/tchap/go-patricia/v2 v2.3.1 // indirect + github.com/tidwall/gjson v1.17.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.1 // indirect github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208 // indirect github.com/trustelem/zxcvbn v1.0.1 // indirect github.com/wk8/go-ordered-map v1.0.0 // indirect diff --git a/go.sum b/go.sum index 9c6d75cb61c..d3747bc41c8 100644 --- a/go.sum +++ b/go.sum @@ -1987,6 +1987,13 @@ github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE github.com/thanhpk/randstr v1.0.4 h1:IN78qu/bR+My+gHCvMEXhR/i5oriVHcTB/BJJIRTsNo= github.com/thejerf/suture/v4 v4.0.2 h1:VxIH/J8uYvqJY1+9fxi5GBfGRkRZ/jlSOP6x9HijFQc= github.com/thejerf/suture/v4 v4.0.2/go.mod h1:g0e8vwskm9tI0jRjxrnA6lSr0q6OfPdWJVX7G5bVWRs= +github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= +github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208 h1:PM5hJF7HVfNWmCjMdEfbuOBNXSVF2cMFGgQTPdKCbwM= github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208/go.mod h1:BzWtXXrXzZUvMacR0oF/fbDDgUPO8L36tDMmRAf14ns= diff --git a/services/graph/pkg/service/v0/graph.go b/services/graph/pkg/service/v0/graph.go index 29d22beccaa..43e5afdd25f 100644 --- a/services/graph/pkg/service/v0/graph.go +++ b/services/graph/pkg/service/v0/graph.go @@ -16,6 +16,11 @@ import ( "github.com/go-chi/chi/v5" "github.com/jellydator/ttlcache/v3" libregraph "github.com/owncloud/libre-graph-api-go" + "go-micro.dev/v4/client" + mevents "go-micro.dev/v4/events" + "go.opentelemetry.io/otel/trace" + "google.golang.org/protobuf/types/known/emptypb" + "github.com/owncloud/ocis/v2/ocis-pkg/keycloak" "github.com/owncloud/ocis/v2/ocis-pkg/log" ehsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/eventhistory/v0" @@ -24,10 +29,6 @@ import ( "github.com/owncloud/ocis/v2/services/graph/pkg/config" "github.com/owncloud/ocis/v2/services/graph/pkg/identity" "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode" - "go-micro.dev/v4/client" - mevents "go-micro.dev/v4/events" - "go.opentelemetry.io/otel/trace" - "google.golang.org/protobuf/types/known/emptypb" ) //go:generate make -C ../../.. generate diff --git a/services/graph/pkg/service/v0/service.go b/services/graph/pkg/service/v0/service.go index 88ba09c0891..c4a99fb2601 100644 --- a/services/graph/pkg/service/v0/service.go +++ b/services/graph/pkg/service/v0/service.go @@ -17,6 +17,8 @@ import ( ldapv3 "github.com/go-ldap/ldap/v3" "github.com/jellydator/ttlcache/v3" libregraph "github.com/owncloud/libre-graph-api-go" + microstore "go-micro.dev/v4/store" + ocisldap "github.com/owncloud/ocis/v2/ocis-pkg/ldap" "github.com/owncloud/ocis/v2/ocis-pkg/registry" "github.com/owncloud/ocis/v2/ocis-pkg/roles" @@ -25,7 +27,6 @@ import ( "github.com/owncloud/ocis/v2/services/graph/pkg/identity" "github.com/owncloud/ocis/v2/services/graph/pkg/identity/ldap" graphm "github.com/owncloud/ocis/v2/services/graph/pkg/middleware" - microstore "go-micro.dev/v4/store" ) const ( @@ -100,6 +101,8 @@ type Service interface { UpdateDrive(w http.ResponseWriter, r *http.Request) DeleteDrive(w http.ResponseWriter, r *http.Request) + ListSharedWithMe(w http.ResponseWriter, r *http.Request) + GetRootDriveChildren(w http.ResponseWriter, r *http.Request) GetDriveItem(w http.ResponseWriter, r *http.Request) GetDriveItemChildren(w http.ResponseWriter, r *http.Request) @@ -199,7 +202,9 @@ func NewService(opts ...Option) (Graph, error) { m.Route(options.Config.HTTP.Root, func(r chi.Router) { r.Use(middleware.StripSlashes) + // fixMe, add beta group to the libre graph api r.Route("/v1.0", func(r chi.Router) { + r.Get("/me/drive/sharedWithMe", svc.ListSharedWithMe) r.Route("/extensions/org.libregraph", func(r chi.Router) { r.Get("/tags", svc.GetTags) r.Put("/tags", svc.AssignTags) diff --git a/services/graph/pkg/service/v0/sharedwithme.go b/services/graph/pkg/service/v0/sharedwithme.go new file mode 100644 index 00000000000..716e32d1293 --- /dev/null +++ b/services/graph/pkg/service/v0/sharedwithme.go @@ -0,0 +1,147 @@ +package svc + +import ( + "context" + "net/http" + "path" + + userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" + collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" + storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + "github.com/go-chi/render" + libregraph "github.com/owncloud/libre-graph-api-go" + + "github.com/owncloud/ocis/v2/services/graph/pkg/identity" + "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode" +) + +// ListSharedWithMe returns a list of all driveItems shared with the user +// +// urls: +// - https://host.docker.internal:9200/ocs/v1.php/apps/files_sharing/api/v1/shares?include_tags=false&state=all&show_hidden=true&shared_with_me=true +// +// todo: +// show-hidden or show_hidden +// +// ambiguity: +// include_tags query param -> web uses it, but what is it for? +// path query param -> web doesn't use it +// share_ref query param -> web doesn't use it +// share_types query param -> web doesn't use it +func (g Graph) ListSharedWithMe(w http.ResponseWriter, r *http.Request) { + showHidden := r.URL.Query().Get("show-hidden") == "true" + ctx := r.Context() + driveItems, err := g.listSharedWithMe(ctx, showHidden) + if err != nil { + errorcode.RenderError(w, r, err) + return + } + + render.Status(r, http.StatusOK) + render.JSON(w, r, &ListResponse{Value: driveItems}) +} +func (g Graph) listSharedWithMe(ctx context.Context, showHidden bool) ([]libregraph.DriveItem, error) { + gatewayClient, err := g.gatewaySelector.Next() + if err != nil { + return nil, err + } + + listReceivedSharesResponse, err := gatewayClient.ListReceivedShares(ctx, &collaboration.ListReceivedSharesRequest{}) + if err != nil { + return nil, err + } + + switch listReceivedSharesResponse.Status.Code { + case rpc.Code_CODE_NOT_FOUND: + return nil, identity.ErrNotFound + } + + var driveItems []libregraph.DriveItem + for _, share := range listReceivedSharesResponse.GetShares() { + if share.Hidden && !showHidden { + continue + } + + stat, err := gatewayClient.Stat(ctx, &storageprovider.StatRequest{Ref: &storageprovider.Reference{ResourceId: share.Share.ResourceId}}) + if err != nil { + // fixMe: return err!? + continue + } + if stat.Status.Code != rpc.Code_CODE_OK { + // fixMe: return err!? + continue + } + + driveItem := &libregraph.DriveItem{ + RemoteItem: &libregraph.RemoteItem{}, + } + + // driveItem.remoteItem + // ✅ createdBy IdentitySet Identity of the user, device, and application which created the item. Read-only. + // ✅ createdDateTime Timestamp Date and time of item creation. Read-only. + // ❌ file File Indicates that the remote item is a file. Read-only. + // ❌ fileSystemInfo FileSystemInfo Information about the remote item from the local file system. Read-only. + // ❌ folder Folder Indicates that the remote item is a folder. Read-only. + // ✅ id String Unique identifier for the remote item in its drive. Read-only. + // ❌ image Image Image metadata, if the item is an image. Read-only. + // ❌ lastModifiedBy IdentitySet Identity of the user, device, and application which last modified the item. Read-only. + // ✅ lastModifiedDateTime Timestamp Date and time the item was last modified. Read-only. + // ✅ name String Optional. Filename of the remote item. Read-only. + // ❌ package Package If present, indicates that this item is a package instead of a folder or file. Packages are treated like files in some contexts and folders in others. Read-only. + // ❌ parentReference ItemReference Properties of the parent of the remote item. Read-only. + // ❌ shared shared Indicates that the item has been shared with others and provides information about the shared state of the item. Read-only. + // ❌ sharepointIds SharepointIds Provides interop between items in OneDrive for Business and SharePoint with the full set of item identifiers. Read-only. + // ✅ size Int64 Size of the remote item. Read-only. + // ❌ specialFolder specialFolder If the current item is also available as a special folder, this facet is returned. Read-only. + // ❌ video Video Video metadata, if the item is a video. Read-only. + // ❌ webDavUrl Url DAV compatible URL for the item. + // ❌ webUrl Url URL that displays the resource in the browser. Read-only. + + if share.Share.Id != nil { + driveItem.RemoteItem.Id = libregraph.PtrString(share.Share.Id.OpaqueId) + } + + if share.Share.Creator != nil { + if getUserResponse, err := gatewayClient.GetUser(ctx, &userpb.GetUserRequest{ + UserId: &userpb.UserId{ + OpaqueId: share.Share.Creator.OpaqueId, + }, + SkipFetchingUserGroups: true, + }); err != nil { + // fixMe: return err!? + continue + } else { + user := libregraph.NewIdentityWithDefaults() + user.SetId(getUserResponse.GetUser().GetId().GetOpaqueId()) + user.SetDisplayName(getUserResponse.GetUser().GetDisplayName()) + + // fixMe: + // - IdentitySet Application + // - IdentitySet Device + // - IdentitySet Group + driveItem.RemoteItem.CreatedBy = &libregraph.IdentitySet{ + User: user, + } + } + } + + if share.Share.Ctime != nil { + driveItem.RemoteItem.CreatedDateTime = libregraph.PtrTime(cs3TimestampToTime(share.Share.Ctime)) + } + + if share.Share.Mtime != nil { + driveItem.RemoteItem.LastModifiedDateTime = libregraph.PtrTime(cs3TimestampToTime(share.Share.Mtime)) + } + + driveItem.RemoteItem.Size = libregraph.PtrInt64(int64(stat.Info.Size)) + + if name := path.Base(stat.Info.Path); name != "" { + driveItem.RemoteItem.Name = libregraph.PtrString(name) + } + + driveItems = append(driveItems, *driveItem) + } + + return driveItems, nil +} diff --git a/services/graph/pkg/service/v0/sharedwithme_test.go b/services/graph/pkg/service/v0/sharedwithme_test.go new file mode 100644 index 00000000000..2da923b2608 --- /dev/null +++ b/services/graph/pkg/service/v0/sharedwithme_test.go @@ -0,0 +1,206 @@ +package svc_test + +import ( + "context" + "net/http" + "net/http/httptest" + + gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + collaborationv1beta1 "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" + providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" + "github.com/cs3org/reva/v2/pkg/rgrpc/status" + "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" + "github.com/cs3org/reva/v2/pkg/storagespace" + cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/stretchr/testify/mock" + "github.com/tidwall/gjson" + "google.golang.org/grpc" + + "github.com/owncloud/ocis/v2/ocis-pkg/shared" + "github.com/owncloud/ocis/v2/services/graph/pkg/config" + "github.com/owncloud/ocis/v2/services/graph/pkg/config/defaults" + identitymocks "github.com/owncloud/ocis/v2/services/graph/pkg/identity/mocks" + service "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0" + "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode" +) + +var _ = Describe("Groups", func() { + var ( + svc service.Service + cfg *config.Config + gatewayClient *cs3mocks.GatewayAPIClient + gatewaySelector pool.Selectable[gateway.GatewayAPIClient] + identityBackend *identitymocks.Backend + ctx context.Context + tape *httptest.ResponseRecorder + ) + + BeforeEach(func() { + pool.RemoveSelector("GatewaySelector" + "com.owncloud.api.gateway") + gatewayClient = &cs3mocks.GatewayAPIClient{} + gatewaySelector = pool.GetSelector[gateway.GatewayAPIClient]( + "GatewaySelector", + "com.owncloud.api.gateway", + func(cc *grpc.ClientConn) gateway.GatewayAPIClient { + return gatewayClient + }, + ) + + identityBackend = &identitymocks.Backend{} + + tape = httptest.NewRecorder() + ctx = context.Background() + + cfg = defaults.FullDefaultConfig() + cfg.Identity.LDAP.CACert = "" // skip the startup checks, we don't use LDAP at all in this tests + cfg.TokenManager.JWTSecret = "loremipsum" + cfg.Commons = &shared.Commons{} + cfg.GRPCClientTLS = &shared.GRPCClientTLS{} + + svc, _ = service.NewService( + service.Config(cfg), + service.WithGatewaySelector(gatewaySelector), + service.WithIdentityBackend(identityBackend), + ) + }) + + Describe("ListSharedWithMe", func() { + var ( + listReceivedSharesResponse *collaborationv1beta1.ListReceivedSharesResponse + statResponse *providerv1beta1.StatResponse + ) + toResourceID := func(in string) *providerv1beta1.ResourceId { + out, err := storagespace.ParseID(in) + Expect(err).To(BeNil()) + + return &out + } + + BeforeEach(func() { + + listReceivedSharesResponse = &collaborationv1beta1.ListReceivedSharesResponse{ + Status: status.NewOK(ctx), + Shares: []*collaborationv1beta1.ReceivedShare{ + { + Share: &collaborationv1beta1.Share{ + Id: &collaborationv1beta1.ShareId{ + OpaqueId: "1:2:3", + }, + ResourceId: toResourceID("4$5!6"), + }, + }, + }, + } + + statResponse = &providerv1beta1.StatResponse{ + Status: status.NewOK(ctx), + Info: &providerv1beta1.ResourceInfo{}, + } + + gatewayClient.On("ListReceivedShares", mock.Anything, mock.Anything).Return(listReceivedSharesResponse, nil) + + gatewayClient.On("Stat", mock.Anything, mock.Anything).Return(func(_ context.Context, r *providerv1beta1.StatRequest, _ ...grpc.CallOption) (*providerv1beta1.StatResponse, error) { + for _, share := range listReceivedSharesResponse.Shares { + if share.Share.ResourceId != r.Ref.ResourceId { + continue + } + + return statResponse, nil + } + + return nil, nil + }) + }) + + It("fails if no received shares were found", func() { + listReceivedSharesResponse.Status = status.NewNotFound(ctx, "msg") + + svc.ListSharedWithMe( + tape, + httptest.NewRequest(http.MethodGet, "/graph/beta/me/drive/sharedWithMe", nil), + ) + + Expect(tape.Code, errorcode.ItemNotFound) + }) + + It("ignores hidden received shares by default", func() { + listReceivedSharesResponse.Shares = append(listReceivedSharesResponse.Shares, &collaborationv1beta1.ReceivedShare{ + Hidden: true, + }) + + svc.ListSharedWithMe( + tape, + httptest.NewRequest(http.MethodGet, "/graph/beta/me/drive/sharedWithMe", nil), + ) + + jsonData := gjson.Get(tape.Body.String(), "value") + + Expect(len(listReceivedSharesResponse.Shares)).To(Equal(2)) + Expect(jsonData.Get("#").Num).To(Equal(float64(1))) + }) + + It("includes hidden shares if explicitly stated", func() { + listReceivedSharesResponse.Shares = append(listReceivedSharesResponse.Shares, &collaborationv1beta1.ReceivedShare{ + Hidden: true, + Share: &collaborationv1beta1.Share{ + ResourceId: toResourceID("7$8!9"), + }, + }) + + svc.ListSharedWithMe( + tape, + httptest.NewRequest(http.MethodGet, "/graph/beta/me/drive/sharedWithMe?show-hidden=true", nil), + ) + + jsonData := gjson.Get(tape.Body.String(), "value") + + Expect(len(listReceivedSharesResponse.Shares)).To(Equal(2)) + Expect(jsonData.Get("#").Num).To(Equal(float64(2))) + }) + + It("populates the driveItem RemoteItem property", func() { + creator := &userv1beta1.User{ + Id: &userv1beta1.UserId{ + OpaqueId: "2699b42d-c6ca-4ce1-90de-89dedfb3022c", + }, + DisplayName: "Cem Kaner", + } + + share := listReceivedSharesResponse.Shares[0].Share + share.Creator = creator.Id + share.Ctime = &typesv1beta1.Timestamp{Seconds: 4000} + share.Mtime = &typesv1beta1.Timestamp{Seconds: 40000} + + statInfo := statResponse.Info + statInfo.Size = 500 + statInfo.Path = "./any/path/to/resource" + + gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(func(_ context.Context, r *userv1beta1.GetUserRequest, _ ...grpc.CallOption) (*userv1beta1.GetUserResponse, error) { + Expect(r.UserId.OpaqueId).To(Equal(creator.Id.OpaqueId)) + Expect(r.SkipFetchingUserGroups).To(BeTrue()) + + return &userv1beta1.GetUserResponse{ + User: creator, + }, nil + }) + + svc.ListSharedWithMe( + tape, + httptest.NewRequest(http.MethodGet, "/graph/beta/me/drive/sharedWithMe", nil), + ) + + jsonData := gjson.Get(tape.Body.String(), "value.0.remoteItem") + Expect(jsonData.Get("id").String()).To(Equal(share.Id.OpaqueId)) + Expect(jsonData.Get("createdBy.user.displayName").String()).To(Equal(creator.DisplayName)) + Expect(jsonData.Get("createdBy.user.id").String()).To(Equal(creator.Id.OpaqueId)) + Expect(jsonData.Get("createdDateTime").String()).To(Equal("1970-01-01T02:06:40+01:00")) + Expect(jsonData.Get("lastModifiedDateTime").String()).To(Equal("1970-01-01T12:06:40+01:00")) + Expect(jsonData.Get("size").Num).To(Equal(float64(statInfo.Size))) + Expect(jsonData.Get("name").String()).To(Equal("resource")) + }) + }) +}) diff --git a/services/proxy/pkg/user/backend/cs3.go b/services/proxy/pkg/user/backend/cs3.go index 70cb9cb8a40..60c3097c62b 100644 --- a/services/proxy/pkg/user/backend/cs3.go +++ b/services/proxy/pkg/user/backend/cs3.go @@ -13,12 +13,13 @@ import ( revactx "github.com/cs3org/reva/v2/pkg/ctx" "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" libregraph "github.com/owncloud/libre-graph-api-go" + "go-micro.dev/v4/selector" + "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/ocis-pkg/oidc" "github.com/owncloud/ocis/v2/ocis-pkg/registry" "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode" "github.com/owncloud/ocis/v2/services/proxy/pkg/config" - "go-micro.dev/v4/selector" ) type cs3backend struct { @@ -144,7 +145,7 @@ func (c *cs3backend) Authenticate(ctx context.Context, username string, password // attributes from the provided `claims` map. On success it returns the new // user. If the user already exist this is not considered an error and the // function will just return the existing user. -func (c *cs3backend) CreateUserFromClaims(ctx context.Context, claims map[string]interface{}) (*cs3.User, error) { +func (c *cs3backend) CreateUserFromClaims(_ context.Context, claims map[string]interface{}) (*cs3.User, error) { gatewayClient, err := c.gatewaySelector.Next() if err != nil { c.logger.Error().Err(err).Msg("could not select next gateway client") @@ -175,7 +176,7 @@ func (c *cs3backend) CreateUserFromClaims(ctx context.Context, claims map[string return nil, fmt.Errorf("Error creating user from claims: %w", err) } - req := lgClient.UsersApi.CreateUser(newctx).User(newUser) + req := lgClient.UsersAPI.CreateUser(newctx).User(newUser) created, resp, err := req.Execute() defer resp.Body.Close() @@ -202,7 +203,7 @@ func (c *cs3backend) CreateUserFromClaims(ctx context.Context, claims map[string // User has been created meanwhile, re-read it to get the user id if reread { c.logger.Debug().Msg("User already exist, re-reading via libregraph") - gureq := lgClient.UserApi.GetUser(newctx, newUser.GetOnPremisesSamAccountName()) + gureq := lgClient.UserAPI.GetUser(newctx, newUser.GetOnPremisesSamAccountName()) created, resp, err = gureq.Execute() defer resp.Body.Close() if err != nil { @@ -216,7 +217,7 @@ func (c *cs3backend) CreateUserFromClaims(ctx context.Context, claims map[string return &cs3UserCreated, nil } -func (c cs3backend) setupLibregraphClient(ctx context.Context, cs3token string) (*libregraph.APIClient, error) { +func (c cs3backend) setupLibregraphClient(_ context.Context, cs3token string) (*libregraph.APIClient, error) { // Use micro registry to resolve next graph service endpoint next, err := c.graphSelector.Select("com.owncloud.graph.graph") if err != nil { @@ -260,7 +261,7 @@ func (c cs3backend) isAlreadyExists(resp *http.Response) (bool, error) { return false, nil } -func (c cs3backend) libregraphUserFromClaims(ctx context.Context, claims map[string]interface{}) (libregraph.User, error) { +func (c cs3backend) libregraphUserFromClaims(_ context.Context, claims map[string]interface{}) (libregraph.User, error) { var ok bool var dn, mail, username string user := libregraph.User{} @@ -280,7 +281,7 @@ func (c cs3backend) libregraphUserFromClaims(ctx context.Context, claims map[str return user, nil } -func (c cs3backend) cs3UserFromLibregraph(ctx context.Context, lu *libregraph.User) cs3.User { +func (c cs3backend) cs3UserFromLibregraph(_ context.Context, lu *libregraph.User) cs3.User { cs3id := cs3.UserId{ Type: cs3.UserType_USER_TYPE_PRIMARY, Idp: c.oidcISS,