1// @author Couchbase <info@couchbase.com>
2// @copyright 2014 Couchbase, Inc.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//      http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16// Package cbauth provides auth{N,Z} for couchbase server services.
17package cbauth
18
19import (
20	"crypto/tls"
21	"fmt"
22	"net/http"
23
24	"github.com/couchbase/cbauth/cbauthimpl"
25)
26
27// TODO: consider API that would allow us to do digest auth behind the
28// scene
29
30// TODO: for GetHTTPServiceAuth consider something more generic such
31// as GetHTTPAuthHeader. Or even maybe RoundTrip. So that we can
32// handle digest auth
33
34// TLSRefreshCallback type describes callback for reinitializing TLSConfig when ssl certificate
35// or client cert auth setting changes.
36type TLSRefreshCallback cbauthimpl.TLSRefreshCallback
37
38// Authenticator is main cbauth interface. It supports both incoming
39// and outgoing auth.
40type Authenticator interface {
41	// AuthWebCreds method extracts credentials from given http request.
42	AuthWebCreds(req *http.Request) (creds Creds, err error)
43	// Auth method constructs credentials from given user and password pair.
44	Auth(user, pwd string) (creds Creds, err error)
45	// GetHTTPServiceAuth returns user/password creds giving
46	// "admin" access to given http service inside couchbase cluster.
47	GetHTTPServiceAuth(hostport string) (user, pwd string, err error)
48	// GetMemcachedServiceAuth returns user/password creds given
49	// "admin" access to given memcached service.
50	GetMemcachedServiceAuth(hostport string) (user, pwd string, err error)
51	// RegisterTLSRefreshCallback registers callback for refreshing TLS Config whenever
52	// SSL certificates are refreshed or when client certificate auth state is changed.
53	RegisterTLSRefreshCallback(callback TLSRefreshCallback) error
54	// GetClientCertAuthType returns the client certificate authentication
55	// type to be used by the web-server.
56	GetClientCertAuthType() (tls.ClientAuthType, error)
57}
58
59// Creds type represents credentials and answers queries on this creds
60// authorized actions. Note: it'll become (possibly much) wider API in
61// future, but it's main purpose right now is to get us started.
62type Creds interface {
63	// Name method returns user name (e.g. for auditing)
64	Name() string
65	// Domain method returns user domain (for auditing)
66	Domain() string
67	// IsAllowed method returns true if the permission is granted
68	// for these credentials
69	IsAllowed(permission string) (bool, error)
70}
71
72var _ Creds = (*cbauthimpl.CredsImpl)(nil)
73
74type authImpl struct {
75	svc *cbauthimpl.Svc
76}
77
78// DBStaleError is kind of error that signals that cbauth internal
79// state is not synchronized with ns_server yet or anymore.
80type DBStaleError struct {
81	Err error
82}
83
84func (e *DBStaleError) Error() string {
85	if e.Err != nil {
86		return fmt.Sprintf("CBAuth database is stale: last reason: %s", e.Err)
87	}
88	return "CBAuth database is stale. Was never updated yet."
89}
90
91// ErrNoAuth is an error that is returned when the user credentials
92// are not recognized
93var ErrNoAuth = cbauthimpl.ErrNoAuth
94
95// UnknownHostPortError is returned from GetMemcachedServiceAuth and
96// GetHTTPServiceAuth calls for unknown host:port arguments.
97type UnknownHostPortError string
98
99func (s UnknownHostPortError) Error() string {
100	return fmt.Sprintf("Unable to find given hostport in cbauth database: `%s'", string(s))
101}
102
103func (a *authImpl) AuthWebCreds(req *http.Request) (creds Creds, err error) {
104	if cbauthimpl.IsAuthTokenPresent(req) {
105		return cbauthimpl.VerifyOnServer(a.svc, req.Header)
106	}
107
108	rv, err := cbauthimpl.MaybeGetCredsFromCert(a.svc, req)
109	if err != nil {
110		return nil, err
111	} else if rv != nil {
112		return rv, nil
113	}
114
115	user, pwd, err := ExtractCreds(req)
116	if err != nil {
117		return nil, err
118	}
119	if user == "" && pwd == "" {
120		return nil, fmt.Errorf("No web credentials found in request.")
121	}
122	return cbauthimpl.VerifyPassword(a.svc, user, pwd)
123}
124
125func (a *authImpl) Auth(user, pwd string) (creds Creds, err error) {
126	return cbauthimpl.VerifyPassword(a.svc, user, pwd)
127}
128
129func (a *authImpl) GetMemcachedServiceAuth(hostport string) (user, pwd string, err error) {
130	host, port, err := SplitHostPort(hostport)
131	if err != nil {
132		return "", "", err
133	}
134	user, _, pwd, err = cbauthimpl.GetCreds(a.svc, host, port)
135	if err == nil && user == "" && pwd == "" {
136		return "", "", UnknownHostPortError(hostport)
137	}
138	return
139}
140
141func (a *authImpl) GetHTTPServiceAuth(hostport string) (user, pwd string, err error) {
142	host, port, err := SplitHostPort(hostport)
143	if err != nil {
144		return "", "", err
145	}
146	_, user, pwd, err = cbauthimpl.GetCreds(a.svc, host, port)
147	if err == nil && user == "" && pwd == "" {
148		return "", "", UnknownHostPortError(hostport)
149	}
150	return
151}
152
153func (a *authImpl) RegisterTLSRefreshCallback(callback TLSRefreshCallback) error {
154	return cbauthimpl.RegisterTLSRefreshCallback(a.svc, cbauthimpl.TLSRefreshCallback(callback))
155}
156
157func (a *authImpl) GetClientCertAuthType() (tls.ClientAuthType, error) {
158	return cbauthimpl.GetClientCertAuthType(a.svc)
159}
160
161var _ Authenticator = (*authImpl)(nil)
162