Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/auth
diff options
context:
space:
mode:
authorJacob Vosmaer (GitLab) <jacob@gitlab.com>2018-04-11 16:14:53 +0300
committerZeger-Jan van de Weg <zegerjan@gitlab.com>2018-04-11 16:14:53 +0300
commitca1f9fc86f7f301dc9dbb8245180de8460d87c06 (patch)
tree96e66c7b77add9ae0eb0955d4a36dc545d4f43e4 /auth
parent4670e7da8549506752627357bb05ad62332274c9 (diff)
Make more of the auth code public so we can reuse it
Diffstat (limited to 'auth')
-rw-r--r--auth/extract_test.go68
-rw-r--r--auth/token.go45
2 files changed, 113 insertions, 0 deletions
diff --git a/auth/extract_test.go b/auth/extract_test.go
new file mode 100644
index 000000000..f2cc3f773
--- /dev/null
+++ b/auth/extract_test.go
@@ -0,0 +1,68 @@
+package gitalyauth
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ "golang.org/x/net/context"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/credentials"
+ "google.golang.org/grpc/metadata"
+ "google.golang.org/grpc/status"
+)
+
+func TestCheckToken(t *testing.T) {
+ secret := "secret 1234"
+
+ testCases := []struct {
+ desc string
+ md metadata.MD
+ code codes.Code
+ }{
+ {
+ desc: "ok",
+ md: credsMD(t, RPCCredentials(secret)),
+ code: codes.OK,
+ },
+ {
+ desc: "denied",
+ md: credsMD(t, RPCCredentials("wrong secret")),
+ code: codes.PermissionDenied,
+ },
+ {
+ desc: "invalid, not bearer",
+ md: credsMD(t, &invalidCreds{"foobar"}),
+ code: codes.Unauthenticated,
+ },
+ {
+ desc: "invalid, bearer but not base64",
+ md: credsMD(t, &invalidCreds{"Bearer foo!!bar"}),
+ code: codes.Unauthenticated,
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *testing.T) {
+ ctx := metadata.NewIncomingContext(context.Background(), tc.md)
+ err := CheckToken(ctx, secret)
+ require.Equal(t, tc.code, status.Code(err), "expected grpc code in error %v", err)
+ })
+ }
+
+}
+
+func credsMD(t *testing.T, creds credentials.PerRPCCredentials) metadata.MD {
+ md, err := creds.GetRequestMetadata(context.Background())
+ require.NoError(t, err)
+ return metadata.New(md)
+}
+
+type invalidCreds struct {
+ authHeader string
+}
+
+func (invalidCreds) RequireTransportSecurity() bool { return false }
+
+func (ic *invalidCreds) GetRequestMetadata(context.Context, ...string) (map[string]string, error) {
+ return map[string]string{"authorization": ic.authHeader}, nil
+}
diff --git a/auth/token.go b/auth/token.go
new file mode 100644
index 000000000..fc13c5bee
--- /dev/null
+++ b/auth/token.go
@@ -0,0 +1,45 @@
+package gitalyauth
+
+import (
+ "crypto/subtle"
+ "encoding/base64"
+
+ "github.com/grpc-ecosystem/go-grpc-middleware/auth"
+ "golang.org/x/net/context"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+)
+
+var (
+ errUnauthenticated = status.Errorf(codes.Unauthenticated, "authentication required")
+ errDenied = status.Errorf(codes.PermissionDenied, "permission denied")
+)
+
+// CheckToken checks the 'authentication' header of incoming gRPC
+// metadata in ctx. It returns nil if and only if the token matches
+// secret.
+func CheckToken(ctx context.Context, secret string) error {
+ if len(secret) == 0 {
+ panic("CheckToken: secret may not be empty")
+ }
+
+ encodedToken, err := grpc_auth.AuthFromMD(ctx, "bearer")
+ if err != nil {
+ return errUnauthenticated
+ }
+
+ token, err := base64.StdEncoding.DecodeString(encodedToken)
+ if err != nil {
+ return errUnauthenticated
+ }
+
+ if !tokensEqual(token, []byte(secret)) {
+ return errDenied
+ }
+
+ return nil
+}
+
+func tokensEqual(tok1, tok2 []byte) bool {
+ return subtle.ConstantTimeCompare(tok1, tok2) == 1
+}