package mtinfo import ( "crypto/rsa" "errors" "io/ioutil" "os" "testing" "time" "github.com/dgrijalva/jwt-go.git" "google.golang.org/grpc/metadata" "golang.org/x/net/context" "google.golang.org/grpc" "github.com/stretchr/testify/assert" auth "src.dualinventive.com/go/authentication-service/grpc" ) const ( someUsername = "some-username" someCompanyCode = "some-company-code" somePassword = "some-password" someUserAgent = "some-user-agent" someToken = "abc" someResetCode = "def" ) var mock *testGrpcAuthClient //nolint:gochecknoglobals var client grpcAuthServiceClient //nolint:gochecknoglobals var pk *rsa.PrivateKey //nolint:gochecknoglobals func TestMain(m *testing.M) { code := m.Run() os.Exit(code) } func TestGrpcAuthServiceClient_Login(t *testing.T) { before(t) mock.resp = &auth.LoginResponse{Token: &auth.Token{Secret: someToken}} tkn, err := client.Login(someUsername, someCompanyCode, somePassword) assert.Nil(t, err) in := mock.in.(*auth.LoginRequest) assert.Equal(t, in.Credentials.Username, someUsername) assert.Equal(t, in.Credentials.CompanyCode, someCompanyCode) assert.Equal(t, in.Credentials.Password, somePassword) assert.Equal(t, tkn, someToken) assert.Equal(t, getValue(mock.ctx, "userAgent"), someUserAgent) } func TestGrpcAuthServiceClient_Login_WhenGrpcErrorMessage(t *testing.T) { before(t) mock.resp = &auth.LoginResponse{} mock.err = errors.New("some-error") _, err := client.Login(someUsername, someCompanyCode, somePassword) assert.NotNil(t, err) assert.EqualError(t, err, "some-error") } func TestGrpcAuthServiceClient_Logout(t *testing.T) { before(t) mock.resp = &auth.Empty{} err := client.Logout(someToken) assert.Nil(t, err) assert.Equal(t, getValue(mock.ctx, "token"), someToken) assert.Equal(t, getValue(mock.ctx, "userAgent"), someUserAgent) } func TestGrpcAuthServiceClient_Logout_WhenGrpcErrorMessage(t *testing.T) { before(t) mock.resp = &auth.Empty{} mock.err = errors.New("some-error") err := client.Logout(someToken) assert.NotNil(t, err) assert.EqualError(t, err, "some-error") } func TestGrpcAuthServiceClient_Me(t *testing.T) { before(t) mock.resp = &auth.MeResponse{User: &auth.User{ UserId: 1, UserName: "test", Company: &auth.Company{Id: 1, Name: "test", Code: "tst"}, Roles: []*auth.Role{{Name: "role", Rights: []string{"right"}}}}} usr, err := client.Me(someToken) assert.Nil(t, err) assert.Equal(t, usr.ID, uint(1)) assert.Equal(t, usr.Name, "test") assert.Equal(t, usr.Company.Name, "test") assert.Equal(t, usr.Company.Code, "tst") assert.Equal(t, usr.Company.ID, uint(1)) assert.Len(t, usr.Roles, 1) assert.Equal(t, usr.Roles[0].Name, "role") assert.Len(t, usr.Roles[0].Rights, 1) assert.Equal(t, usr.Roles[0].Rights[0], Right("right")) assert.Equal(t, getValue(mock.ctx, "token"), someToken) assert.Equal(t, getValue(mock.ctx, "userAgent"), someUserAgent) } func TestGrpcAuthServiceClient_Me_WhenGrpcErrorMessage(t *testing.T) { before(t) mock.resp = &auth.MeResponse{} mock.err = errors.New("some-error") _, err := client.Me(someToken) assert.NotNil(t, err) assert.EqualError(t, err, "some-error") } func TestGrpcAuthServiceClient_RequestPasswordReset(t *testing.T) { before(t) mock.resp = &auth.Empty{} err := client.RequestPasswordReset(someUsername) assert.Nil(t, err) in := mock.in.(*auth.RequestPasswordResetRequest) assert.Equal(t, in.UserName, someUsername) } func TestGrpcAuthServiceClient_RequestPasswordReset_WhenGrpcErrorMessage(t *testing.T) { before(t) mock.resp = &auth.Empty{} mock.err = errors.New("some-error") err := client.RequestPasswordReset(someToken) assert.NotNil(t, err) assert.EqualError(t, err, "some-error") } func TestGrpcAuthServiceClient_RedeemPasswordReset(t *testing.T) { before(t) mock.resp = &auth.Empty{} err := client.RedeemPasswordReset(someUsername, someResetCode, somePassword, somePassword) assert.Nil(t, err) in := mock.in.(*auth.RedeemPasswordResetRequest) assert.Equal(t, in.UserName, someUsername) assert.Equal(t, in.ResetCode, someResetCode) assert.Equal(t, in.Password, somePassword) assert.Equal(t, in.PasswordVerify, somePassword) } func TestGrpcAuthServiceClient_RedeemPasswordReset_WhenGrpcErrorMessage(t *testing.T) { before(t) mock.resp = &auth.Empty{} mock.err = errors.New("some-error") err := client.RedeemPasswordReset(someUsername, someResetCode, somePassword, somePassword) assert.NotNil(t, err) assert.EqualError(t, err, "some-error") } func TestGrpcAuthServiceClient_ListTokens(t *testing.T) { before(t) mock.resp = &auth.ListTokensResponse{Tokens: []*auth.OpaqueToken{ {UserAgent: "2", OpaqueId: "abc"}, {UserAgent: "1", OpaqueId: "def"}, }} list, err := client.ListTokens(someToken) assert.Nil(t, err) assert.Len(t, list, 2) assert.Equal(t, list["abc"], "2") assert.Equal(t, list["def"], "1") assert.Equal(t, getValue(mock.ctx, "token"), someToken) assert.Equal(t, getValue(mock.ctx, "userAgent"), someUserAgent) } func TestGrpcAuthServiceClient_ListTokens_WhenGrpcErrorMessage(t *testing.T) { before(t) mock.resp = &auth.ListTokensResponse{} mock.err = errors.New("some-error") _, err := client.ListTokens(someToken) assert.NotNil(t, err) assert.EqualError(t, err, "some-error") } func TestGrpcAuthServiceClient_DeleteToken(t *testing.T) { before(t) mock.resp = &auth.Empty{} err := client.DeleteToken(someToken, "1") assert.Nil(t, err) in := mock.in.(*auth.DeleteTokenRequest) assert.Equal(t, in.OpaqueId, "1") assert.Equal(t, getValue(mock.ctx, "token"), someToken) assert.Equal(t, getValue(mock.ctx, "userAgent"), someUserAgent) } func TestGrpcAuthServiceClient_DeleteToken_WhenGrpcErrorMessage(t *testing.T) { before(t) mock.resp = &auth.Empty{} mock.err = errors.New("some-error") err := client.DeleteToken(someToken, "") assert.NotNil(t, err) assert.EqualError(t, err, "some-error") } func TestGrpcAuthServiceClient_VerifyTokenRemotely(t *testing.T) { before(t) mock.resp = &auth.VerifyTokenResponse{Valid: true} valid, err := client.VerifyTokenRemotely(someToken) assert.Nil(t, err) assert.True(t, valid) assert.Equal(t, getValue(mock.ctx, "token"), someToken) assert.Equal(t, getValue(mock.ctx, "userAgent"), someUserAgent) } func TestGrpcAuthServiceClient_VerifyTokenRemotely_WhenGrpcErrorMessage(t *testing.T) { before(t) mock.resp = &auth.VerifyTokenResponse{} mock.err = errors.New("some-error") valid, err := client.VerifyTokenRemotely(someToken) assert.NotNil(t, err) assert.False(t, valid) assert.EqualError(t, err, "some-error") } func TestGrpcAuthServiceClient_VerifyToken_WithPublicKey_DecryptsLocally(t *testing.T) { before(t) valid, err := client.VerifyToken(randomToken(t)) assert.Nil(t, err) assert.True(t, valid) assert.Nil(t, mock.ctx) } func TestGrpcAuthServiceClient_VerifyToken_WithPublicKeyWithValidRights_DecryptsLocally(t *testing.T) { before(t) valid, err := client.VerifyToken(randomToken(t), "a", "b") assert.Nil(t, err) assert.True(t, valid) assert.Nil(t, mock.ctx) } func TestGrpcAuthServiceClient_VerifyToken_WithPublicKeyWithInvalidRights_DecryptsLocally(t *testing.T) { before(t) valid, err := client.VerifyToken(randomToken(t), "a", "b", "d") assert.Nil(t, err) assert.False(t, valid) assert.Nil(t, mock.ctx) } func TestGrpcAuthServiceClient_VerifyToken_WithBadPublicKey_DecryptsLocallyAndReturnsError(t *testing.T) { before(t) badPublicKey, err := configurePublicKey("./testdata/key_rsa_bad.pub") assert.Nil(t, err) client.publicKey = badPublicKey valid, err := client.VerifyToken(randomToken(t)) assert.NotNil(t, err) assert.False(t, valid) assert.EqualError(t, err, "error parsing jwt token: crypto/rsa: verification error") } func TestGrpcAuthServiceClient_VerifyTokenWithoutPublicKey_DecryptsRemotely(t *testing.T) { before(t) mock.resp = &auth.VerifyTokenResponse{Valid: true} client.publicKey = nil valid, err := client.VerifyToken(someToken) assert.Nil(t, err) assert.True(t, valid) in := mock.in.(*auth.VerifyTokenRequest) assert.Len(t, in.Rights, 0) assert.Equal(t, getValue(mock.ctx, "token"), someToken) assert.Equal(t, getValue(mock.ctx, "userAgent"), someUserAgent) } func TestGrpcAuthServiceClient_VerifyTokenWithoutPublicKeyWithRights_DecryptsRemotely(t *testing.T) { before(t) mock.resp = &auth.VerifyTokenResponse{Valid: true} client.publicKey = nil valid, err := client.VerifyToken(someToken, "a", "b", "c") assert.Nil(t, err) assert.True(t, valid) in := mock.in.(*auth.VerifyTokenRequest) assert.Len(t, in.Rights, 3) assert.Contains(t, in.Rights, "a") assert.Contains(t, in.Rights, "b") assert.Contains(t, in.Rights, "c") assert.Equal(t, getValue(mock.ctx, "token"), someToken) assert.Equal(t, getValue(mock.ctx, "userAgent"), someUserAgent) } func before(t *testing.T) { publicKey, err := configurePublicKey("./testdata/key_rsa.pub") assert.Nil(t, err) privateKey, err := configurePrivateKey("./testdata/key_rsa") assert.Nil(t, err) mock = &testGrpcAuthClient{} client = grpcAuthServiceClient{ client: mock, userAgent: someUserAgent, publicKey: publicKey, } pk = privateKey } func configurePrivateKey(filepath string) (*rsa.PrivateKey, error) { f, err := ioutil.ReadFile(filepath) if err != nil { return nil, err } return jwt.ParseRSAPrivateKeyFromPEM(f) } func randomToken(t *testing.T) string { expirationTime := time.Now().Add(time.Second * 3) claims := claims{ Rights: []string{"a", "b", "c"}, StandardClaims: jwt.StandardClaims{ ExpiresAt: expirationTime.Unix(), }, } token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims) tokenString, err := token.SignedString(pk) assert.Nil(t, err) return tokenString } type claims struct { Rights []string jwt.StandardClaims } func getValue(ctx context.Context, s string) string { md, ok := metadata.FromOutgoingContext(ctx) if !ok { return "" } raw := md.Get(s) if raw == nil { return "" } if len(raw) == 0 { return "" } return raw[0] } type testGrpcAuthClient struct { ctx context.Context in interface{} opts []grpc.CallOption resp interface{} err error } func (m *testGrpcAuthClient) Login( ctx context.Context, in *auth.LoginRequest, opts ...grpc.CallOption) (*auth.LoginResponse, error) { m.ctx = ctx m.in = in m.opts = opts return m.resp.(*auth.LoginResponse), m.err } func (m *testGrpcAuthClient) Logout( ctx context.Context, in *auth.Empty, opts ...grpc.CallOption) (*auth.Empty, error) { m.ctx = ctx m.in = in m.opts = opts return m.resp.(*auth.Empty), m.err } func (m *testGrpcAuthClient) VerifyToken( ctx context.Context, in *auth.VerifyTokenRequest, opts ...grpc.CallOption) (*auth.VerifyTokenResponse, error) { m.ctx = ctx m.in = in m.opts = opts return m.resp.(*auth.VerifyTokenResponse), m.err } func (m *testGrpcAuthClient) Me( ctx context.Context, in *auth.Empty, opts ...grpc.CallOption) (*auth.MeResponse, error) { m.ctx = ctx m.in = in m.opts = opts return m.resp.(*auth.MeResponse), m.err } func (m *testGrpcAuthClient) RequestPasswordReset( ctx context.Context, in *auth.RequestPasswordResetRequest, opts ...grpc.CallOption) (*auth.Empty, error) { m.ctx = ctx m.in = in m.opts = opts return m.resp.(*auth.Empty), m.err } func (m *testGrpcAuthClient) RedeemPasswordReset( ctx context.Context, in *auth.RedeemPasswordResetRequest, opts ...grpc.CallOption) (*auth.Empty, error) { m.ctx = ctx m.in = in m.opts = opts return m.resp.(*auth.Empty), m.err } func (m *testGrpcAuthClient) ListTokens( ctx context.Context, in *auth.Empty, opts ...grpc.CallOption) (*auth.ListTokensResponse, error) { m.ctx = ctx m.in = in m.opts = opts return m.resp.(*auth.ListTokensResponse), m.err } func (m *testGrpcAuthClient) DeleteToken( ctx context.Context, in *auth.DeleteTokenRequest, opts ...grpc.CallOption) (*auth.Empty, error) { m.ctx = ctx m.in = in m.opts = opts return m.resp.(*auth.Empty), m.err }