package svc_test import ( "bytes" "context" "encoding/json" "errors" "net/http" "net/http/httptest" gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" "github.com/go-ldap/ldap/v3" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" libregraph "github.com/opencloud-eu/libre-graph-api-go" revactx "github.com/opencloud-eu/reva/v2/pkg/ctx" "github.com/opencloud-eu/reva/v2/pkg/rgrpc/status" "github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool" cs3mocks "github.com/opencloud-eu/reva/v2/tests/cs3mocks/mocks" "github.com/stretchr/testify/mock" "google.golang.org/grpc" "github.com/qsfera/server/pkg/log" "github.com/qsfera/server/pkg/shared" "github.com/qsfera/server/services/graph/mocks" "github.com/qsfera/server/services/graph/pkg/config" "github.com/qsfera/server/services/graph/pkg/config/defaults" "github.com/qsfera/server/services/graph/pkg/identity" identitymocks "github.com/qsfera/server/services/graph/pkg/identity/mocks" service "github.com/qsfera/server/services/graph/pkg/service/v0" ) var _ = Describe("Users changing their own password", func() { var ( svc service.Service gatewayClient *cs3mocks.GatewayAPIClient gatewaySelector pool.Selectable[gateway.GatewayAPIClient] ldapClient *identitymocks.Client ldapConfig config.LDAP identityBackend identity.Backend eventsPublisher mocks.Publisher ctx context.Context cfg *config.Config user *userv1beta1.User err error ) JustBeforeEach(func() { ctx = context.Background() cfg = defaults.FullDefaultConfig() cfg.TokenManager.JWTSecret = "loremipsum" cfg.GRPCClientTLS = &shared.GRPCClientTLS{} pool.RemoveSelector("GatewaySelector" + "qsfera.api.gateway") gatewayClient = &cs3mocks.GatewayAPIClient{} gatewaySelector = pool.GetSelector[gateway.GatewayAPIClient]( "GatewaySelector", "qsfera.api.gateway", func(cc grpc.ClientConnInterface) gateway.GatewayAPIClient { return gatewayClient }, ) ldapClient = mockedLDAPClient() ldapConfig = config.LDAP{ WriteEnabled: true, UserDisplayNameAttribute: "displayName", UserNameAttribute: "uid", UserEmailAttribute: "mail", UserIDAttribute: "qsferaUUID", UserSearchScope: "sub", GroupNameAttribute: "cn", GroupIDAttribute: "qsferaUUID", GroupSearchScope: "sub", } logger := log.NewLogger() identityBackend, err = identity.NewLDAPBackend(ldapClient, ldapConfig, &logger) Expect(err).To(BeNil()) eventsPublisher = mocks.Publisher{} var err error svc, err = service.NewService( service.Config(cfg), service.WithGatewaySelector(gatewaySelector), service.WithIdentityBackend(identityBackend), service.EventsPublisher(&eventsPublisher), ) Expect(err).ToNot(HaveOccurred()) user = &userv1beta1.User{ Id: &userv1beta1.UserId{ OpaqueId: "user", }, } ctx = revactx.ContextSetUser(ctx, user) eventsPublisher.On("Publish", mock.Anything, mock.Anything, mock.Anything).Return(nil) }) It("fails if no user in context", func() { r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/changePassword", nil) rr := httptest.NewRecorder() svc.ChangeOwnPassword(rr, r) Expect(rr.Code).To(Equal(http.StatusInternalServerError)) }) DescribeTable("changing the password", func(current string, newpw string, authresult string, expected int) { switch authresult { case "error": gatewayClient.On("Authenticate", mock.Anything, mock.Anything).Return(nil, errors.New("fail")) case "deny": gatewayClient.On("Authenticate", mock.Anything, mock.Anything).Return(&gateway.AuthenticateResponse{ Status: status.NewPermissionDenied(ctx, errors.New("wrong password"), "wrong password"), Token: "authtoken", }, nil) default: gatewayClient.On("Authenticate", mock.Anything, mock.Anything).Return(&gateway.AuthenticateResponse{ Status: status.NewOK(ctx), Token: "authtoken", }, nil) } cpw := libregraph.NewPasswordChangeWithDefaults() cpw.SetCurrentPassword(current) cpw.SetNewPassword(newpw) body, _ := json.Marshal(cpw) b := bytes.NewBuffer(body) r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/me/changePassword", b).WithContext(ctx) rr := httptest.NewRecorder() svc.ChangeOwnPassword(rr, r) Expect(rr.Code).To(Equal(expected)) }, Entry("fails when current password is empty", "", "newpassword", "", http.StatusBadRequest), Entry("fails when new password is empty", "currentpassword", "", "", http.StatusBadRequest), Entry("fails when current and new password are equal", "password", "password", "", http.StatusBadRequest), Entry("fails authentication with current password errors", "currentpassword", "newpassword", "error", http.StatusInternalServerError), Entry("fails when current password is wrong", "currentpassword", "newpassword", "deny", http.StatusBadRequest), Entry("succeeds when current password is correct", "currentpassword", "newpassword", "", http.StatusNoContent), ) }) func mockedLDAPClient() *identitymocks.Client { lm := &identitymocks.Client{} userEntry := ldap.NewEntry("uid=test", map[string][]string{ "uid": {"test"}, "displayName": {"test"}, "mail": {"test@example.org"}, }) lm.On("Search", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). Return( &ldap.SearchResult{Entries: []*ldap.Entry{userEntry}}, nil) mr := ldap.NewModifyRequest("uid=test", nil) mr.Changes = []ldap.Change{ { Operation: ldap.ReplaceAttribute, Modification: ldap.PartialAttribute{ Type: "userPassword", Vals: []string{"newpassword"}, }, }, } lm.On("Modify", mr).Return(nil) return lm }