func TestX509Verifier(t *testing.T) {
	testCases := map[string]struct {
		Insecure bool
		Certs    []*x509.Certificate

		Opts x509.VerifyOptions

		ExpectOK  bool
		ExpectErr bool
	}{
		"non-tls": {
			Insecure: true,

			ExpectOK:  false,
			ExpectErr: false,
		},

		"tls, no certs": {
			ExpectOK:  false,
			ExpectErr: false,
		},

		"self signed": {
			Opts:  getDefaultVerifyOptions(t),
			Certs: getCerts(t, selfSignedCert),

			ExpectErr: true,
		},

		"server cert disallowed": {
			Opts:  getDefaultVerifyOptions(t),
			Certs: getCerts(t, serverCert),

			ExpectErr: true,
		},
		"server cert allowing non-client cert usages": {
			Opts:  x509.VerifyOptions{Roots: getRootCertPool(t)},
			Certs: getCerts(t, serverCert),

			ExpectOK:  true,
			ExpectErr: false,
		},

		"valid client cert": {
			Opts:  getDefaultVerifyOptions(t),
			Certs: getCerts(t, clientCNCert),

			ExpectOK:  true,
			ExpectErr: false,
		},

		"future cert": {
			Opts: x509.VerifyOptions{
				CurrentTime: time.Now().Add(time.Duration(-100 * time.Hour * 24 * 365)),
				Roots:       getRootCertPool(t),
			},
			Certs: getCerts(t, clientCNCert),

			ExpectOK:  false,
			ExpectErr: true,
		},
		"expired cert": {
			Opts: x509.VerifyOptions{
				CurrentTime: time.Now().Add(time.Duration(100 * time.Hour * 24 * 365)),
				Roots:       getRootCertPool(t),
			},
			Certs: getCerts(t, clientCNCert),

			ExpectOK:  false,
			ExpectErr: true,
		},
	}

	for k, testCase := range testCases {
		req, _ := http.NewRequest("GET", "/", nil)
		if !testCase.Insecure {
			req.TLS = &tls.ConnectionState{PeerCertificates: testCase.Certs}
		}

		authCall := false
		auth := authenticator.RequestFunc(func(req *http.Request) (user.Info, bool, error) {
			authCall = true
			return &user.DefaultInfo{Name: "innerauth"}, true, nil
		})

		a := NewVerifier(testCase.Opts, auth)

		user, ok, err := a.AuthenticateRequest(req)

		if testCase.ExpectErr && err == nil {
			t.Errorf("%s: Expected error, got none", k)
			continue
		}
		if !testCase.ExpectErr && err != nil {
			t.Errorf("%s: Got unexpected error: %v", k, err)
			continue
		}

		if testCase.ExpectOK != ok {
			t.Errorf("%s: Expected ok=%v, got %v", k, testCase.ExpectOK, ok)
			continue
		}

		if testCase.ExpectOK {
			if !authCall {
				t.Errorf("%s: Expected inner auth called, wasn't", k)
				continue
			}
			if "innerauth" != user.GetName() {
				t.Errorf("%s: Expected user.name=%v, got %v", k, "innerauth", user.GetName())
				continue
			}
		} else {
			if authCall {
				t.Errorf("%s: Expected inner auth not to be called, was", k)
				continue
			}
		}
	}
}
func TestX509(t *testing.T) {
	testCases := map[string]struct {
		Insecure bool
		Certs    []*x509.Certificate

		Opts x509.VerifyOptions
		User UserConversion

		ExpectUserName string
		ExpectOK       bool
		ExpectErr      bool
	}{
		"non-tls": {
			Insecure: true,

			ExpectOK:  false,
			ExpectErr: false,
		},

		"tls, no certs": {
			ExpectOK:  false,
			ExpectErr: false,
		},

		"self signed": {
			Opts:  getDefaultVerifyOptions(t),
			Certs: getCerts(t, selfSignedCert),
			User:  CommonNameUserConversion,

			ExpectErr: true,
		},

		"server cert": {
			Opts:  getDefaultVerifyOptions(t),
			Certs: getCerts(t, serverCert),
			User:  CommonNameUserConversion,

			ExpectErr: true,
		},
		"server cert allowing non-client cert usages": {
			Opts:  x509.VerifyOptions{Roots: getRootCertPool(t)},
			Certs: getCerts(t, serverCert),
			User:  CommonNameUserConversion,

			ExpectUserName: "******",
			ExpectOK:       true,
			ExpectErr:      false,
		},

		"common name": {
			Opts:  getDefaultVerifyOptions(t),
			Certs: getCerts(t, clientCNCert),
			User:  CommonNameUserConversion,

			ExpectUserName: "******",
			ExpectOK:       true,
			ExpectErr:      false,
		},

		"empty dns": {
			Opts:  getDefaultVerifyOptions(t),
			Certs: getCerts(t, clientCNCert),
			User:  DNSNameUserConversion,

			ExpectOK:  false,
			ExpectErr: false,
		},
		"dns": {
			Opts:  getDefaultVerifyOptions(t),
			Certs: getCerts(t, clientDNSCert),
			User:  DNSNameUserConversion,

			ExpectUserName: "******",
			ExpectOK:       true,
			ExpectErr:      false,
		},

		"empty email": {
			Opts:  getDefaultVerifyOptions(t),
			Certs: getCerts(t, clientCNCert),
			User:  EmailAddressUserConversion,

			ExpectOK:  false,
			ExpectErr: false,
		},
		"email": {
			Opts:  getDefaultVerifyOptions(t),
			Certs: getCerts(t, clientEmailCert),
			User:  EmailAddressUserConversion,

			ExpectUserName: "******",
			ExpectOK:       true,
			ExpectErr:      false,
		},

		"custom conversion error": {
			Opts:  getDefaultVerifyOptions(t),
			Certs: getCerts(t, clientCNCert),
			User: UserConversionFunc(func(chain []*x509.Certificate) (user.Info, bool, error) {
				return nil, false, errors.New("custom error")
			}),

			ExpectOK:  false,
			ExpectErr: true,
		},
		"custom conversion success": {
			Opts:  getDefaultVerifyOptions(t),
			Certs: getCerts(t, clientCNCert),
			User: UserConversionFunc(func(chain []*x509.Certificate) (user.Info, bool, error) {
				return &user.DefaultInfo{Name: "custom"}, true, nil
			}),

			ExpectUserName: "******",
			ExpectOK:       true,
			ExpectErr:      false,
		},

		"future cert": {
			Opts: x509.VerifyOptions{
				CurrentTime: time.Now().Add(time.Duration(-100 * time.Hour * 24 * 365)),
				Roots:       getRootCertPool(t),
			},
			Certs: getCerts(t, clientCNCert),
			User:  CommonNameUserConversion,

			ExpectOK:  false,
			ExpectErr: true,
		},
		"expired cert": {
			Opts: x509.VerifyOptions{
				CurrentTime: time.Now().Add(time.Duration(100 * time.Hour * 24 * 365)),
				Roots:       getRootCertPool(t),
			},
			Certs: getCerts(t, clientCNCert),
			User:  CommonNameUserConversion,

			ExpectOK:  false,
			ExpectErr: true,
		},
	}

	for k, testCase := range testCases {
		req, _ := http.NewRequest("GET", "/", nil)
		if !testCase.Insecure {
			req.TLS = &tls.ConnectionState{PeerCertificates: testCase.Certs}
		}

		a := New(testCase.Opts, testCase.User)

		user, ok, err := a.AuthenticateRequest(req)

		if testCase.ExpectErr && err == nil {
			t.Errorf("%s: Expected error, got none", k)
			continue
		}
		if !testCase.ExpectErr && err != nil {
			t.Errorf("%s: Got unexpected error: %v", k, err)
			continue
		}

		if testCase.ExpectOK != ok {
			t.Errorf("%s: Expected ok=%v, got %v", k, testCase.ExpectOK, ok)
			continue
		}

		if testCase.ExpectOK {
			if testCase.ExpectUserName != user.GetName() {
				t.Errorf("%s: Expected user.name=%v, got %v", k, testCase.ExpectUserName, user.GetName())
				continue
			}
		}
	}
}
Example #3
0
	return nil, false, kerrors.NewAggregate(errlist)
}

// DefaultVerifyOptions returns VerifyOptions that use the system root certificates, current time,
// and requires certificates to be valid for client auth (x509.ExtKeyUsageClientAuth)
func DefaultVerifyOptions() x509.VerifyOptions {
	return x509.VerifyOptions{
		KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
	}
}

// SubjectToUserConversion calls SubjectToUser on the subject of the first certificate in the chain.
// If the resulting user has no name, it returns nil, false, nil
var SubjectToUserConversion = UserConversionFunc(func(chain []*x509.Certificate) (user.Info, bool, error) {
	user := SubjectToUser(chain[0].Subject)
	if len(user.GetName()) == 0 {
		return nil, false, nil
	}
	return user, true, nil
})

// CommonNameUserConversion builds user info from a certificate chain using the subject's CommonName
var CommonNameUserConversion = UserConversionFunc(func(chain []*x509.Certificate) (user.Info, bool, error) {
	if len(chain[0].Subject.CommonName) == 0 {
		return nil, false, nil
	}
	return &user.DefaultInfo{Name: chain[0].Subject.CommonName}, true, nil
})

// DNSNameUserConversion builds user info from a certificate chain using the first DNSName on the certificate
var DNSNameUserConversion = UserConversionFunc(func(chain []*x509.Certificate) (user.Info, bool, error) {
Example #4
0
func TestRequestHeader(t *testing.T) {
	testcases := map[string]struct {
		ConfiguredHeaders []string
		RequestHeaders    http.Header
		ExpectedUsername  string
	}{
		"empty": {
			ExpectedUsername: "",
		},
		"no match": {
			ConfiguredHeaders: []string{"X-Remote-User"},
			ExpectedUsername:  "",
		},
		"match": {
			ConfiguredHeaders: []string{"X-Remote-User"},
			RequestHeaders:    http.Header{"X-Remote-User": {"Bob"}},
			ExpectedUsername:  "******",
		},
		"exact match": {
			ConfiguredHeaders: []string{"X-Remote-User"},
			RequestHeaders: http.Header{
				"Prefixed-X-Remote-User-With-Suffix": {"Bob"},
				"X-Remote-User-With-Suffix":          {"Bob"},
			},
			ExpectedUsername: "",
		},
		"first match": {
			ConfiguredHeaders: []string{
				"X-Remote-User",
				"A-Second-X-Remote-User",
				"Another-X-Remote-User",
			},
			RequestHeaders: http.Header{
				"X-Remote-User":          {"", "First header, second value"},
				"A-Second-X-Remote-User": {"Second header, first value", "Second header, second value"},
				"Another-X-Remote-User":  {"Third header, first value"}},
			ExpectedUsername: "******",
		},
		"case-insensitive": {
			ConfiguredHeaders: []string{"x-REMOTE-user"},             // configured headers can be case-insensitive
			RequestHeaders:    http.Header{"X-Remote-User": {"Bob"}}, // the parsed headers are normalized by the http package
			ExpectedUsername:  "******",
		},
	}

	for k, testcase := range testcases {
		mapper := &TestUserIdentityMapper{}
		auth := NewAuthenticator("testprovider", &Config{testcase.ConfiguredHeaders}, mapper)
		req := &http.Request{Header: testcase.RequestHeaders}

		user, ok, err := auth.AuthenticateRequest(req)
		if testcase.ExpectedUsername == "" {
			if ok {
				t.Errorf("%s: Didn't expect user, authentication succeeded", k)
				continue
			}
		}
		if testcase.ExpectedUsername != "" {
			if err != nil {
				t.Errorf("%s: Expected user, got error: %v", k, err)
				continue
			}
			if !ok {
				t.Errorf("%s: Expected user, auth failed", k)
				continue
			}
			if testcase.ExpectedUsername != user.GetName() {
				t.Errorf("%s: Expected username %s, got %s", k, testcase.ExpectedUsername, user.GetName())
				continue
			}
		}
	}
}
Example #5
0
func TestBasicAuth(t *testing.T) {
	testCases := map[string]struct {
		Header   string
		Password testPassword

		ExpectedCalled   bool
		ExpectedUsername string
		ExpectedPassword string

		ExpectedUser string
		ExpectedOK   bool
		ExpectedErr  bool
	}{
		"no header": {
			Header: "",
		},
		"non-basic header": {
			Header: "Bearer foo",
		},
		"empty value basic header": {
			Header: "Basic",
		},
		"whitespace value basic header": {
			Header: "Basic  ",
		},
		"non base-64 basic header": {
			Header:      "Basic !@#$",
			ExpectedErr: true,
		},
		"malformed basic header": {
			Header:      "Basic " + base64.StdEncoding.EncodeToString([]byte("user_without_password")),
			ExpectedErr: true,
		},
		"empty password basic header": {
			Header:           "Basic " + base64.StdEncoding.EncodeToString([]byte("user_with_empty_password:"******"user_with_empty_password",
			ExpectedPassword: "",
		},
		"valid basic header": {
			Header:           "Basic " + base64.StdEncoding.EncodeToString([]byte("myuser:mypassword:withcolon")),
			ExpectedCalled:   true,
			ExpectedUsername: "******",
			ExpectedPassword: "******",
		},
		"password auth returned user": {
			Header:           "Basic " + base64.StdEncoding.EncodeToString([]byte("myuser:mypw")),
			Password:         testPassword{User: &user.DefaultInfo{Name: "returneduser"}, OK: true},
			ExpectedCalled:   true,
			ExpectedUsername: "******",
			ExpectedPassword: "******",
			ExpectedUser:     "******",
			ExpectedOK:       true,
		},
		"password auth returned error": {
			Header:           "Basic " + base64.StdEncoding.EncodeToString([]byte("myuser:mypw")),
			Password:         testPassword{Err: errors.New("auth error")},
			ExpectedCalled:   true,
			ExpectedUsername: "******",
			ExpectedPassword: "******",
			ExpectedErr:      true,
		},
	}

	for k, testCase := range testCases {
		password := testCase.Password
		auth := authenticator.Request(New(&password))

		req, _ := http.NewRequest("GET", "/", nil)
		if testCase.Header != "" {
			req.Header.Set("Authorization", testCase.Header)
		}

		user, ok, err := auth.AuthenticateRequest(req)

		if testCase.ExpectedCalled != password.Called {
			t.Fatalf("%s: Expected called=%v, got %v", k, testCase.ExpectedCalled, password.Called)
			continue
		}
		if testCase.ExpectedUsername != password.Username {
			t.Fatalf("%s: Expected called with username=%v, got %v", k, testCase.ExpectedUsername, password.Username)
			continue
		}
		if testCase.ExpectedPassword != password.Password {
			t.Fatalf("%s: Expected called with password=%v, got %v", k, testCase.ExpectedPassword, password.Password)
			continue
		}

		if testCase.ExpectedErr != (err != nil) {
			t.Fatalf("%s: Expected err=%v, got err=%v", k, testCase.ExpectedErr, err)
			continue
		}
		if testCase.ExpectedOK != ok {
			t.Fatalf("%s: Expected ok=%v, got ok=%v", k, testCase.ExpectedOK, ok)
			continue
		}
		if testCase.ExpectedUser != "" && testCase.ExpectedUser != user.GetName() {
			t.Fatalf("%s: Expected user.GetName()=%v, got %v", k, testCase.ExpectedUser, user.GetName())
			continue
		}
	}
}