455 lines
12 KiB
Go
455 lines
12 KiB
Go
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
|
|
}
|