1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2017-2018 Couchbase, Inc.
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 
18 #include <libcouchbase/couchbase.h>
19 #include "auth-priv.h"
20 #include <sstream>
21 
22 using namespace lcb;
23 
24 lcb_AUTHENTICATOR *
lcbauth_new()25 lcbauth_new()
26 {
27     return new Authenticator();
28 }
29 
30 lcb_error_t
lcbauth_add_pass(lcb_AUTHENTICATOR * auth,const char * u,const char * p,int flags)31 lcbauth_add_pass(lcb_AUTHENTICATOR *auth, const char *u, const char *p, int flags)
32 {
33     return auth->add(u, p, flags);
34 }
35 
36 lcb_error_t
add(const char * u,const char * p,int flags)37 Authenticator::add(const char *u, const char *p, int flags)
38 {
39     if (!u) {
40         return LCB_EINVAL;
41     }
42 
43     if (!(flags & (LCBAUTH_F_BUCKET|LCBAUTH_F_CLUSTER))) {
44         return LCB_EINVAL;
45     }
46 
47     if (m_mode == LCBAUTH_MODE_RBAC && (flags & LCBAUTH_F_BUCKET)) {
48         return LCB_OPTIONS_CONFLICT;
49     }
50 
51     if (flags & LCBAUTH_F_CLUSTER) {
52         if (p) {
53             m_username = u;
54             m_password = p;
55         } else {
56             m_username.clear();
57             m_password.clear();
58         }
59     }
60 
61     if (flags & LCBAUTH_F_BUCKET) {
62         if (p) {
63             m_buckets[u] = p;
64         } else {
65             m_buckets.erase(u);
66         }
67     }
68 
69     return LCB_SUCCESS;
70 }
71 
72 static const std::string EmptyString;
73 
cache_key(const char * host,const char * port,const char * bucket)74 std::string cache_key(const char *host, const char *port, const char *bucket) {
75     std::stringstream key;
76     key << ":" << (host ? host : "?nullhost?");
77     key << ":" << (port ? port : "?nullport?");
78     key << ":" << (bucket ? bucket : "?nullbucket?");
79     return key.str();
80 }
81 
username_for(const char * host,const char * port,const char * bucket,bool use_cache)82 const std::string Authenticator::username_for(const char *host, const char *port, const char *bucket, bool use_cache)
83 {
84     switch (m_mode) {
85         case LCBAUTH_MODE_RBAC:
86             return m_username;
87         case LCBAUTH_MODE_DYNAMIC:
88             if (m_usercb != NULL) {
89                 if (!use_cache) {
90                     return m_usercb(m_cookie, host, port, bucket);
91                 }
92                 std::string key = cache_key(host, port, bucket);
93                 if (user_cache_.find(key) == user_cache_.end()) {
94                     std::string username = m_usercb(m_cookie, host, port, bucket);
95                     user_cache_[key] = username;
96                     return username;
97                 } else {
98                     return user_cache_[key];
99                 }
100             }
101             break;
102         case LCBAUTH_MODE_CLASSIC:
103             // Find bucket specific credentials:
104             const Map::const_iterator it = m_buckets.find(bucket);
105             if (it != m_buckets.end()) {
106                 return it->first;
107             }
108             break;
109     }
110     return EmptyString;
111 }
112 
password_for(const char * host,const char * port,const char * bucket,bool use_cache)113 const std::string Authenticator::password_for(const char *host, const char *port, const char *bucket, bool use_cache)
114 {
115     switch (m_mode) {
116         case LCBAUTH_MODE_RBAC:
117             return m_password;
118         case LCBAUTH_MODE_DYNAMIC:
119             if (m_passcb != NULL) {
120                 if (!use_cache) {
121                     return m_passcb(m_cookie, host, port, bucket);
122                 }
123                 std::string key = cache_key(host, port, bucket);
124                 if (pass_cache_.find(key) == pass_cache_.end()) {
125                     std::string password = m_passcb(m_cookie, host, port, bucket);
126                     pass_cache_[key] = password;
127                     return password;
128                 } else {
129                     return pass_cache_[key];
130                 }
131             }
132             break;
133         case LCBAUTH_MODE_CLASSIC:
134             const Map::const_iterator it = m_buckets.find(bucket);
135             if (it != m_buckets.end()) {
136                 return it->second;
137             }
138             break;
139     }
140     return EmptyString;
141 }
142 
invalidate_cache_for(const char * host,const char * port,const char * bucket)143 void Authenticator::invalidate_cache_for(const char *host, const char *port, const char *bucket)
144 {
145     if (m_mode == LCBAUTH_MODE_DYNAMIC) {
146         std::string key = cache_key(host, port, bucket);
147         pass_cache_.erase(key);
148         user_cache_.erase(key);
149     }
150 }
151 
reset_cache()152 void Authenticator::reset_cache()
153 {
154     pass_cache_.clear();
155     user_cache_.clear();
156 }
157 
158 void
lcbauth_ref(lcb_AUTHENTICATOR * auth)159 lcbauth_ref(lcb_AUTHENTICATOR *auth)
160 {
161     auth->incref();
162 }
163 
164 void
lcbauth_unref(lcb_AUTHENTICATOR * auth)165 lcbauth_unref(lcb_AUTHENTICATOR *auth)
166 {
167     auth->decref();
168 }
169 
Authenticator(const Authenticator & other)170 Authenticator::Authenticator(const Authenticator &other)
171     : m_buckets(other.m_buckets), m_username(other.m_username), m_password(other.m_password), m_refcount(1),
172       m_mode(other.m_mode), m_usercb(other.m_usercb), m_passcb(other.m_passcb), m_cookie(other.m_cookie),
173       user_cache_(), pass_cache_()
174 {
175 }
176 
177 lcb_AUTHENTICATOR *
lcbauth_clone(const lcb_AUTHENTICATOR * src)178 lcbauth_clone(const lcb_AUTHENTICATOR *src) {
179     return new Authenticator(*src);
180 }
181 
182 lcb_error_t
lcbauth_set_mode(lcb_AUTHENTICATOR * src,lcbauth_MODE mode)183 lcbauth_set_mode(lcb_AUTHENTICATOR *src, lcbauth_MODE mode) {
184     return src->set_mode(mode);
185 }
186 
lcbauth_set_callbacks(lcb_AUTHENTICATOR * auth,void * cookie,lcb_AUTHCALLBACK usercb,lcb_AUTHCALLBACK passcb)187 lcb_error_t lcbauth_set_callbacks(lcb_AUTHENTICATOR *auth, void *cookie, lcb_AUTHCALLBACK usercb,
188                                   lcb_AUTHCALLBACK passcb)
189 {
190     return auth->set_callbacks(cookie, usercb, passcb);
191 }
192