Skip to content
This repository has been archived by the owner on Mar 11, 2021. It is now read-only.

Commit

Permalink
expose endpoint to deactivate user (#799)
Browse files Browse the repository at this point in the history
using mocks to verify that controller calls the UserService

also, make sure that all mocks are generated in `test/generated` pkg

fixes #780

Signed-off-by: Xavier Coulon <[email protected]>
  • Loading branch information
xcoulon authored Mar 1, 2019
1 parent d9b7b20 commit 90c741f
Show file tree
Hide file tree
Showing 13 changed files with 160 additions and 38 deletions.
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,10 @@ $(MINIMOCK_BIN):
.PHONY: generate-minimock
generate-minimock: deps $(MINIMOCK_BIN) ## Generate Minimock sources. Only necessary after clean or if changes occurred in interfaces.
@echo "Generating mocks..."
@-mkdir -p test/service
@$(MINIMOCK_BIN) -i github.com/fabric8-services/fabric8-auth/application/service.NotificationService,github.com/fabric8-services/fabric8-auth/application/service.WITService,github.com/fabric8-services/fabric8-auth/application/service.ClusterService,github.com/fabric8-services/fabric8-auth/application/service.AuthenticationProviderService -o ./test/service/ -s ".go"
@-mkdir -p test/token/oauth
@$(MINIMOCK_BIN) -i github.com/fabric8-services/fabric8-auth/authentication/provider.IdentityProvider -o ./test/token/oauth/ -s ".go"
@-mkdir -p test/generated/application/service
@$(MINIMOCK_BIN) -i github.com/fabric8-services/fabric8-auth/application/service.NotificationService,github.com/fabric8-services/fabric8-auth/application/service.WITService,github.com/fabric8-services/fabric8-auth/application/service.ClusterService,github.com/fabric8-services/fabric8-auth/application/service.AuthenticationProviderService,github.com/fabric8-services/fabric8-auth/application/service.UserService -o ./test/generated/application/service/ -s ".go"
@-mkdir -p test/generated/authentication
@$(MINIMOCK_BIN) -i github.com/fabric8-services/fabric8-auth/authentication/provider.IdentityProvider -o ./test/generated/authentication/ -s ".go"
@-mkdir -p test/generated/authorization/token/manager
@$(MINIMOCK_BIN) -i github.com/fabric8-services/fabric8-auth/authorization/token/manager.TokenManagerConfiguration -o ./test/generated/authorization/token/manager/ -s ".go"
@-mkdir -p test/generated/application/service
Expand Down
22 changes: 19 additions & 3 deletions application/service/factory/service_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ type ServiceFactory struct {
notificationServiceFunc func() service.NotificationService // the function to call when `NotificationService()` is called on this factory
clusterServiceFunc func() service.ClusterService
authProviderServiceFunc func() service.AuthenticationProviderService
userServiceFunc func() service.UserService
}

// Option an option to configure the Service Factory
Expand Down Expand Up @@ -187,6 +188,17 @@ func WithAuthenticationProviderService(s service.AuthenticationProviderService)
}
}

// WithUserService overrides the default function that returns the UserService,
// so that instead, a mock implementation can be used
func WithUserService(s service.UserService) Option {
return func(f *ServiceFactory) {
f.userServiceFunc = func() service.UserService {
return s
}
}
}

// NewServiceFactory returns a new ServiceFactory which can be configured with the options to replace the default implementations of some services
func NewServiceFactory(producer servicecontext.ServiceContextProducer, config *configuration.ConfigurationData, options ...Option) *ServiceFactory {
f := &ServiceFactory{contextProducer: producer, config: config}
// default function to return an instance of WIT Service
Expand All @@ -205,11 +217,15 @@ func NewServiceFactory(producer servicecontext.ServiceContextProducer, config *c
f.clusterServiceFunc = func() service.ClusterService {
return clusterservice.NewClusterService(f.getContext(), f.config)
}
// default function to return an instance of Cluster Service
// default function to return an instance of Auth Service
f.authProviderServiceFunc = func() service.AuthenticationProviderService {
return providerservice.NewAuthenticationProviderService(f.getContext(), f.config)
}
log.Info(nil, map[string]interface{}{}, "configuring a new service factory with %d options", len(options))
// default function to return an instance of User Service
f.userServiceFunc = func() service.UserService {
return userservice.NewUserService(f.getContext())
}
log.Info(nil, map[string]interface{}{}, "configuring a new service factory with %d option(s)", len(options))
// and options
for _, opt := range options {
opt(f)
Expand Down Expand Up @@ -274,7 +290,7 @@ func (f *ServiceFactory) SpaceService() service.SpaceService {
}

func (f *ServiceFactory) UserService() service.UserService {
return userservice.NewUserService(f.getContext())
return f.userServiceFunc()
}

func (f *ServiceFactory) UserProfileService() service.UserProfileService {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import (
"context"
"errors"
"fmt"
token2 "github.com/fabric8-services/fabric8-auth/authorization/token"
"net/http"
"net/http/httptest"
"net/url"
"os"
"testing"
"time"

token2 "github.com/fabric8-services/fabric8-auth/authorization/token"

"github.com/fabric8-services/fabric8-auth/rest"

"github.com/fabric8-services/fabric8-auth/app"
Expand All @@ -28,10 +29,10 @@ import (
"github.com/fabric8-services/fabric8-auth/jsonapi"
"github.com/fabric8-services/fabric8-auth/resource"
testsupport "github.com/fabric8-services/fabric8-auth/test"
testoauth "github.com/fabric8-services/fabric8-auth/test/generated/authentication"
testtoken "github.com/fabric8-services/fabric8-auth/test/token"
testoauth "github.com/fabric8-services/fabric8-auth/test/token/oauth"

"github.com/dgrijalva/jwt-go"
jwt "github.com/dgrijalva/jwt-go"
"github.com/goadesign/goa"
"github.com/goadesign/goa/uuid"
_ "github.com/lib/pq"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
"github.com/fabric8-services/fabric8-auth/notification"
"github.com/fabric8-services/fabric8-auth/rest"
"github.com/fabric8-services/fabric8-auth/test"
testservice "github.com/fabric8-services/fabric8-auth/test/service"
testservice "github.com/fabric8-services/fabric8-auth/test/generated/application/service"

"github.com/satori/go.uuid"
"github.com/stretchr/testify/require"
Expand Down
2 changes: 1 addition & 1 deletion controller/invitation_blackbox_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/fabric8-services/fabric8-auth/gormtestsupport"

testsupport "github.com/fabric8-services/fabric8-auth/test"
testservice "github.com/fabric8-services/fabric8-auth/test/service"
testservice "github.com/fabric8-services/fabric8-auth/test/generated/application/service"

"github.com/goadesign/goa"

Expand Down
20 changes: 20 additions & 0 deletions controller/namedusers.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,23 @@ func (c *NamedusersController) Deprovision(ctx *app.DeprovisionNamedusersContext

return ctx.OK(ConvertToAppUser(ctx.RequestData, &identity.User, identity, true))
}

// Deactivate runs the deactivate action.
func (c *NamedusersController) Deactivate(ctx *app.DeactivateNamedusersContext) error {
isSvcAccount := token.IsSpecificServiceAccount(ctx, token.OnlineRegistration)
if !isSvcAccount {
log.Error(ctx, nil, "the account is not an authorized service account allowed to deprovision users")
return jsonapi.JSONErrorResponse(ctx, errors.NewForbiddenError("account not authorized to deprovision users"))
}

identity, err := c.app.UserService().DeactivateUser(ctx, ctx.Username)
if err != nil {
log.Error(ctx, map[string]interface{}{
"err": err,
"username": ctx.Username,
}, "error occurred while deactivating user")
return jsonapi.JSONErrorResponse(ctx, err)
}

return ctx.OK(ConvertToAppUser(ctx.RequestData, &identity.User, identity, true))
}
107 changes: 89 additions & 18 deletions controller/namedusers_blackbox_test.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
package controller_test

import (
"context"
"testing"

"github.com/fabric8-services/fabric8-auth/application/service/factory"
"github.com/fabric8-services/fabric8-auth/gormapplication"

"github.com/fabric8-services/fabric8-auth/app/test"
"github.com/fabric8-services/fabric8-auth/authentication/account/repository"
. "github.com/fabric8-services/fabric8-auth/controller"
"github.com/fabric8-services/fabric8-auth/controller"
"github.com/fabric8-services/fabric8-auth/errors"
"github.com/fabric8-services/fabric8-auth/gormtestsupport"
testsupport "github.com/fabric8-services/fabric8-auth/test"
testservice "github.com/fabric8-services/fabric8-auth/test/generated/application/service"
uuid "github.com/satori/go.uuid"

"github.com/goadesign/goa"
"github.com/satori/go.uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)
Expand All @@ -30,16 +35,16 @@ func (s *NamedUsersControllerTestSuite) SetupTest() {
s.tenantService = &dummyTenantService{}
}

func (s *NamedUsersControllerTestSuite) SecuredServiceAccountController(identity repository.Identity) (*goa.Service, *NamedusersController) {
func (s *NamedUsersControllerTestSuite) SecuredServiceAccountController(identity repository.Identity) (*goa.Service, *controller.NamedusersController) {
svc := testsupport.ServiceAsServiceAccountUser("Namedusers-ServiceAccount-Service", identity)
controller := NewNamedusersController(svc, s.Application, s.Configuration, s.tenantService)
return svc, controller
ctrl := controller.NewNamedusersController(svc, s.Application, s.Configuration, s.tenantService)
return svc, ctrl
}

func (s *NamedUsersControllerTestSuite) SecuredController(identity repository.Identity) (*goa.Service, *NamedusersController) {
func (s *NamedUsersControllerTestSuite) SecuredController(identity repository.Identity) (*goa.Service, *controller.NamedusersController) {
svc := testsupport.ServiceAsUser("Users-Service", identity)
controller := NewNamedusersController(svc, s.Application, s.Configuration, s.tenantService)
return svc, controller
ctrl := controller.NewNamedusersController(svc, s.Application, s.Configuration, s.tenantService)
return svc, ctrl
}

func (s *NamedUsersControllerTestSuite) TestDeprovisionOK() {
Expand All @@ -53,32 +58,32 @@ func (s *NamedUsersControllerTestSuite) TestDeprovisionOK() {
}

func (s *NamedUsersControllerTestSuite) TestDeprovisionFailsForUnknownUser() {
svc, controller := s.SecuredServiceAccountController(testsupport.TestOnlineRegistrationAppIdentity)
test.DeprovisionNamedusersNotFound(s.T(), svc.Context, svc, controller, uuid.NewV4().String())
svc, ctrl := s.SecuredServiceAccountController(testsupport.TestOnlineRegistrationAppIdentity)
test.DeprovisionNamedusersNotFound(s.T(), svc.Context, svc, ctrl, uuid.NewV4().String())
}

func (s *NamedUsersControllerTestSuite) TestDeprovisionFailsForUnauthorizedIdentity() {
userToDeprovision := s.Graph.CreateUser()

// Another service account can't deprovision
svc, controller := s.SecuredServiceAccountController(testsupport.TestTenantIdentity)
test.DeprovisionNamedusersForbidden(s.T(), svc.Context, svc, controller, userToDeprovision.Identity().Username)
svc, ctrl := s.SecuredServiceAccountController(testsupport.TestTenantIdentity)
test.DeprovisionNamedusersForbidden(s.T(), svc.Context, svc, ctrl, userToDeprovision.Identity().Username)

// Regular user can't deprovision either
svc, controller = s.SecuredController(*s.Graph.CreateUser().Identity())
test.DeprovisionNamedusersForbidden(s.T(), svc.Context, svc, controller, userToDeprovision.Identity().Username)
svc, ctrl = s.SecuredController(*s.Graph.CreateUser().Identity())
test.DeprovisionNamedusersForbidden(s.T(), svc.Context, svc, ctrl, userToDeprovision.Identity().Username)

// If no token present in the context then fails too
_, controller = s.SecuredServiceAccountController(testsupport.TestOnlineRegistrationAppIdentity)
test.DeprovisionNamedusersForbidden(s.T(), nil, nil, controller, userToDeprovision.Identity().Username)
_, ctrl = s.SecuredServiceAccountController(testsupport.TestOnlineRegistrationAppIdentity)
test.DeprovisionNamedusersForbidden(s.T(), nil, nil, ctrl, userToDeprovision.Identity().Username)
}

func (s *NamedUsersControllerTestSuite) checkDeprovisionOK() {
userToDeprovision := s.Graph.CreateUser()
userToStayIntact := s.Graph.CreateUser()

svc, controller := s.SecuredServiceAccountController(testsupport.TestOnlineRegistrationAppIdentity)
_, result := test.DeprovisionNamedusersOK(s.T(), svc.Context, svc, controller, userToDeprovision.Identity().Username)
svc, ctrl := s.SecuredServiceAccountController(testsupport.TestOnlineRegistrationAppIdentity)
_, result := test.DeprovisionNamedusersOK(s.T(), svc.Context, svc, ctrl, userToDeprovision.Identity().Username)

// Check if tenant service was called
assert.Equal(s.T(), userToDeprovision.IdentityID(), s.tenantService.identityID)
Expand All @@ -96,3 +101,69 @@ func (s *NamedUsersControllerTestSuite) checkDeprovisionOK() {
assert.Equal(s.T(), false, loadedUser.User().Deprovisioned)
testsupport.AssertIdentityEqual(s.T(), userToStayIntact.Identity(), loadedUser.Identity())
}

func (s *NamedUsersControllerTestSuite) TestDeactivateUser() {

s.T().Run("ok", func(t *testing.T) {
// given
userServiceMock := testservice.NewUserServiceMock(t)
app := gormapplication.NewGormDB(s.DB, s.Configuration, s.Wrappers, factory.WithUserService(userServiceMock))
svc := testsupport.ServiceAsServiceAccountUser("Users-Service", testsupport.TestOnlineRegistrationAppIdentity)
ctrl := controller.NewNamedusersController(svc, app, s.Configuration, s.tenantService)
identity := &repository.Identity{
ID: uuid.NewV4(),
Username: "user-to-deactivate",
User: repository.User{
ID: uuid.NewV4(),
},
}
var usernameArg string
userServiceMock.DeactivateUserFunc = func(ctx context.Context, username string) (*repository.Identity, error) {
usernameArg = username
return identity, nil
}
// userServiceMock.DeactivateUserMock.Expect(svc.Context, identity.Username)
// when
test.DeactivateNamedusersOK(t, svc.Context, svc, ctrl, "user-to-deactivate")
// then
// verify that the `UserService.DeactivateUser` func was called once...
assert.Equal(t, 1, int(userServiceMock.DeactivateUserCounter))
// ... with the expected `username` argument
assert.Equal(t, "user-to-deactivate", usernameArg)
})

s.T().Run("failures", func(t *testing.T) {

t.Run("invalid service account", func(t *testing.T) {
// given
svc, ctrl := s.SecuredServiceAccountController(testsupport.TestAdminConsoleIdentity)
// when
test.DeactivateNamedusersForbidden(t, context.Background(), svc, ctrl, "missing-token-user")

})

t.Run("missing token", func(t *testing.T) {
// given
svc, ctrl := s.SecuredServiceAccountController(testsupport.TestOnlineRegistrationAppIdentity)
// when
test.DeactivateNamedusersForbidden(t, context.Background(), svc, ctrl, "missing-token-user")
})

t.Run("unknown identity", func(t *testing.T) {
// given
userServiceMock := testservice.NewUserServiceMock(t)
app := gormapplication.NewGormDB(s.DB, s.Configuration, s.Wrappers, factory.WithUserService(userServiceMock))
svc := testsupport.ServiceAsServiceAccountUser("Users-Service", testsupport.TestOnlineRegistrationAppIdentity)
ctrl := controller.NewNamedusersController(svc, app, s.Configuration, s.tenantService)
// defer userServiceMock.Finish()
userServiceMock.DeactivateUserFunc = func(ctx context.Context, username string) (*repository.Identity, error) {
return nil, errors.NewNotFoundErrorFromString("user not found")
}
// when
test.DeactivateNamedusersNotFound(t, svc.Context, svc, ctrl, "unknown-user")
// then
// verify that the `UserService.DeactivateUser` func was called once...
assert.Equal(t, 1, int(userServiceMock.DeactivateUserCounter))
})
})
}
2 changes: 1 addition & 1 deletion controller/token_blackbox_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ import (
"github.com/fabric8-services/fabric8-auth/errors"
"github.com/fabric8-services/fabric8-auth/gormtestsupport"
testsupport "github.com/fabric8-services/fabric8-auth/test"
testservice "github.com/fabric8-services/fabric8-auth/test/generated/application/service"
testjwt "github.com/fabric8-services/fabric8-auth/test/jwt"
testservice "github.com/fabric8-services/fabric8-auth/test/service"
testtoken "github.com/fabric8-services/fabric8-auth/test/token"

"github.com/goadesign/goa"
Expand Down
2 changes: 1 addition & 1 deletion controller/users_blackbox_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import (
"github.com/fabric8-services/fabric8-auth/resource"
"github.com/fabric8-services/fabric8-auth/rest"
testsupport "github.com/fabric8-services/fabric8-auth/test"
testservice "github.com/fabric8-services/fabric8-auth/test/service"
testservice "github.com/fabric8-services/fabric8-auth/test/generated/application/service"
"github.com/goadesign/goa"
"github.com/jinzhu/gorm"
"github.com/satori/go.uuid"
Expand Down
17 changes: 17 additions & 0 deletions design/namedusers.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,21 @@ var _ = a.Resource("namedusers", func() {
a.Response(d.Unauthorized, JSONAPIErrors)
a.Response(d.Forbidden, JSONAPIErrors)
})
a.Action("deactivate", func() {
a.Security("jwt")
a.Routing(
a.PATCH("/:username/deactivate"),
)
a.Description("deactivate the user")
a.Params(func() {
a.Param("username", d.String, "Username")
})
a.Response(d.OK, func() {
a.Media(showUser)
})
a.Response(d.InternalServerError, JSONAPIErrors)
a.Response(d.NotFound, JSONAPIErrors)
a.Response(d.Unauthorized, JSONAPIErrors)
a.Response(d.Forbidden, JSONAPIErrors)
})
})
2 changes: 1 addition & 1 deletion test/clusterservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"github.com/fabric8-services/fabric8-auth/cluster"
clusterservice "github.com/fabric8-services/fabric8-auth/cluster/service"
"github.com/fabric8-services/fabric8-auth/rest"
testservice "github.com/fabric8-services/fabric8-auth/test/service"
testservice "github.com/fabric8-services/fabric8-auth/test/generated/application/service"

"github.com/gojuno/minimock"
"github.com/satori/go.uuid"
Expand Down
5 changes: 1 addition & 4 deletions test/graph/organization_wrapper.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package graph

import (
"fmt"

account "github.com/fabric8-services/fabric8-auth/authentication/account/repository"
"github.com/fabric8-services/fabric8-auth/authorization"
resource "github.com/fabric8-services/fabric8-auth/authorization/resource/repository"
"github.com/satori/go.uuid"
uuid "github.com/satori/go.uuid"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -64,7 +62,6 @@ func newOrganizationWrapper(g *TestGraph, params []interface{}) interface{} {
organizationResource.resource.ResourceType = *resourceType
w.resource = organizationResource.Resource()
w.Identity().IdentityResource = *w.resource
fmt.Printf("loaded organization with resource=%v\n", w.resource)
return &w
}

Expand Down
2 changes: 1 addition & 1 deletion test/witservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"context"
"github.com/fabric8-services/fabric8-auth/app"
"github.com/fabric8-services/fabric8-auth/authentication/account/repository"
testservice "github.com/fabric8-services/fabric8-auth/test/service"
testservice "github.com/fabric8-services/fabric8-auth/test/generated/application/service"
"github.com/fabric8-services/fabric8-auth/wit"
goauuid "github.com/goadesign/goa/uuid"
"github.com/gojuno/minimock"
Expand Down

0 comments on commit 90c741f

Please sign in to comment.