blob: 083ae6673ddceb7409369c268786d4faead3e8ba [file] [log] [blame]
// Copyright 2021 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package xsrf
import (
"context"
"net/http"
"net/http/httptest"
"strings"
"testing"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"go.chromium.org/luci/auth/identity"
"go.chromium.org/luci/grpc/discovery"
"go.chromium.org/luci/grpc/prpc"
"go.chromium.org/luci/server/auth"
"go.chromium.org/luci/server/auth/authtest"
"go.chromium.org/luci/server/router"
. "github.com/smartystreets/goconvey/convey"
)
const (
fakeIdentity = identity.Identity("user:someone@example.com")
authKey = "Fake-Auth-Header"
)
func TestInterceptor(t *testing.T) {
t.Parallel()
Convey("Works", t, func() {
ctx := makeContext()
ctx = authtest.MockAuthConfig(ctx)
authMethod1 := &testAuthMethod{expect: "1"}
authMethod2 := &testAuthMethod{expect: "2"}
rpcSrv := &prpc.Server{
Authenticator: &auth.Authenticator{
Methods: []auth.Method{authMethod1, authMethod2},
},
}
// Install the interceptor that checks the token **only** when authMethod1
// is used.
rpcSrv.UnaryServerInterceptor = Interceptor(authMethod1)
// We need some API to call in the test. Reuse Discovery API for that since
// it is simple enough and have no side effects.
discovery.Enable(rpcSrv)
// Expose pRPC API via test HTTP server.
router := router.NewWithRootContext(ctx)
rpcSrv.InstallHandlers(router, nil)
httpSrv := httptest.NewServer(router)
defer httpSrv.Close()
// Create a discovery client targeting our test server.
apiClient := discovery.NewDiscoveryPRPCClient(&prpc.Client{
Host: strings.TrimPrefix(httpSrv.URL, "http://"),
Options: &prpc.Options{
Insecure: true, // not using HTTPS
},
})
call := func(md ...string) codes.Code {
ctx := metadata.NewOutgoingContext(ctx, metadata.Pairs(md...))
_, err := apiClient.Describe(ctx, &discovery.Void{})
return status.Code(err)
}
goodToken, err := Token(auth.WithState(ctx, &authtest.FakeState{
Identity: fakeIdentity,
}))
So(err, ShouldBeNil)
badToken, err := Token(auth.WithState(ctx, &authtest.FakeState{
Identity: "user:someone-else@example.com",
}))
So(err, ShouldBeNil)
// A token is checked, and only when using method "1".
So(call(authKey, "1", XSRFTokenMetadataKey, goodToken), ShouldEqual, codes.OK)
So(call(authKey, "1", XSRFTokenMetadataKey, "", XSRFTokenMetadataKey, goodToken), ShouldEqual, codes.OK)
So(call(authKey, "1", XSRFTokenMetadataKey, badToken), ShouldEqual, codes.Unauthenticated)
So(call(authKey, "1", XSRFTokenMetadataKey, ""), ShouldEqual, codes.Unauthenticated)
So(call(authKey, "1"), ShouldEqual, codes.Unauthenticated)
// When using method "2" the token is ignored.
So(call(authKey, "2", XSRFTokenMetadataKey, goodToken), ShouldEqual, codes.OK)
So(call(authKey, "2", XSRFTokenMetadataKey, badToken), ShouldEqual, codes.OK)
So(call(authKey, "2", XSRFTokenMetadataKey, ""), ShouldEqual, codes.OK)
So(call(authKey, "2"), ShouldEqual, codes.OK)
})
}
type testAuthMethod struct {
expect string
}
func (t *testAuthMethod) Authenticate(ctx context.Context, req *http.Request) (*auth.User, auth.Session, error) {
if req.Header.Get(authKey) == t.expect {
return &auth.User{Identity: fakeIdentity}, nil, nil
}
return nil, nil, nil // skip this method
}