37 namespace SaslClientStrings
39 static constexpr std::string_view login_challenge_1 {
"Username:",9U} ;
40 static constexpr std::string_view login_challenge_2 {
"Password:",9U} ;
47class GAuth::SaslClientImp
50 using Response = SaslClient::Response ;
51 SaslClientImp(
const SaslClientSecrets & ,
const std::string & ) ;
52 bool validSelector( std::string_view )
const ;
53 bool mustAuthenticate( std::string_view )
const ;
54 std::string mechanism(
const G::StringArray & , std::string_view )
const ;
55 Response initialResponse( std::string_view selector , std::size_t )
const ;
56 Response response( std::string_view mechanism , std::string_view challenge , std::string_view selector )
const ;
58 std::string mechanism()
const ;
59 std::string id()
const ;
60 std::string info()
const ;
61 static bool match(
const G::StringArray & mechanisms ,
const std::string & ) ;
64 ~SaslClientImp() = default ;
65 SaslClientImp(
const SaslClientImp & ) = delete ;
66 SaslClientImp( SaslClientImp && ) = delete ;
67 SaslClientImp & operator=(
const SaslClientImp & ) = delete ;
68 SaslClientImp & operator=( SaslClientImp && ) = delete ;
71 const SaslClientSecrets & m_secrets ;
72 std::string m_config ;
74 mutable std::string m_info ;
75 mutable std::string m_id ;
82GAuth::SaslClientImp::SaslClientImp(
const SaslClientSecrets & secrets ,
83 const std::string & sasl_client_config ) :
85 m_config(sasl_client_config) ,
91std::string GAuth::SaslClientImp::mechanism(
const G::StringArray & server_mechanisms , std::string_view selector )
const
100 if( m_secrets.clientSecret(
"plain",selector).valid() )
102 our_list = Cram::hashTypes(
"CRAM-" ,
false ) ;
106 our_list = Cram::hashTypes(
"CRAM-" ,
true ) ;
107 for(
auto p = our_list.begin() ; p != our_list.end() ; )
109 std::string type = (*p).substr( 5U ) ;
110 if( m_secrets.clientSecret(type,selector).valid() )
113 p = our_list.erase( p ) ;
116 if( m_secrets.clientSecret(
"oauth",selector).valid() )
118 our_list.emplace_back(
"XOAUTH2" ) ;
120 if( m_secrets.clientSecret(
"plain",selector).valid() )
122 our_list.push_back( PLAIN ) ;
123 our_list.push_back( LOGIN ) ;
127 if( !m_config.empty() )
138 m_mechanisms.clear() ;
139 for(
auto & our_mechanism : our_list )
141 if( match(server_mechanisms,our_mechanism) )
143 m_mechanisms.push_back( our_mechanism ) ;
147 G_DEBUG(
"GAuth::SaslClientImp::mechanism: server mechanisms: [" <<
G::Str::join(
",",server_mechanisms) <<
"]" ) ;
148 G_DEBUG(
"GAuth::SaslClientImp::mechanism: our mechanisms: [" <<
G::Str::join(
",",our_list) <<
"]" ) ;
149 G_DEBUG(
"GAuth::SaslClientImp::mechanism: usable mechanisms: [" <<
G::Str::join(
",",m_mechanisms) <<
"]" ) ;
151 return m_mechanisms.empty() ? std::string() : m_mechanisms.at(0U) ;
154bool GAuth::SaslClientImp::next()
156 if( !m_mechanisms.empty() )
157 m_mechanisms.erase( m_mechanisms.begin() ) ;
158 return !m_mechanisms.empty() ;
161std::string GAuth::SaslClientImp::mechanism()
const
163 return m_mechanisms.empty() ? std::string() : m_mechanisms.at(0U) ;
172 if( m_mechanisms.empty() || m_mechanisms[0U].find(
"CRAM-") == 0U )
175 const std::string & m = m_mechanisms[0] ;
176 Response rsp = response( m , m ==
"LOGIN" ? SaslClientStrings::login_challenge_1 : std::string_view() , selector ) ;
177 if( rsp.error || rsp.data.size() > limit )
184 std::string_view challenge , std::string_view selector )
const
187 rsp.sensitive = true ;
191 Secret secret = Secret::none() ;
192 if( mechanism.find(
"CRAM-") == 0U )
194 std::string_view hash_type = mechanism.substr( 5U ) ;
195 secret = m_secrets.clientSecret( hash_type , selector ) ;
196 if( !secret.valid() )
197 secret = m_secrets.clientSecret(
"plain" , selector ) ;
198 rsp.data = Cram::response( hash_type ,
true , secret , challenge , secret.id() ) ;
199 rsp.error = rsp.data.empty() ;
202 else if( mechanism ==
"APOP"_sv )
204 secret = m_secrets.clientSecret(
"MD5"_sv , selector ) ;
205 rsp.data = Cram::response(
"MD5"_sv ,
false , secret , challenge , secret.id() ) ;
206 rsp.error = rsp.data.empty() ;
209 else if( mechanism == PLAIN )
211 secret = m_secrets.clientSecret(
"plain"_sv , selector ) ;
212 rsp.data = std::string(1U,
'\0').append(secret.id()).append(1U,
'\0').append(secret.secret()) ;
213 rsp.error = !secret.valid() ;
216 else if( mechanism == LOGIN && challenge == SaslClientStrings::login_challenge_1 )
218 secret = m_secrets.clientSecret(
"plain"_sv , selector ) ;
219 rsp.data = secret.id() ;
220 rsp.error = !secret.valid() ;
222 rsp.sensitive = false ;
224 else if( mechanism == LOGIN && challenge == SaslClientStrings::login_challenge_2 )
226 secret = m_secrets.clientSecret(
"plain"_sv , selector ) ;
227 rsp.data = secret.secret() ;
228 rsp.error = !secret.valid() ;
231 else if( mechanism ==
"XOAUTH2"_sv && challenge.empty() )
233 secret = m_secrets.clientSecret(
"oauth"_sv , selector ) ;
234 rsp.data = secret.secret() ;
235 rsp.error = !secret.valid() ;
238 else if( mechanism ==
"XOAUTH2"_sv )
240 secret = m_secrets.clientSecret(
"oauth"_sv , selector ) ;
244 rsp.sensitive = false ;
250 .assign(
"using mechanism [")
253 .append(secret.info()) ;
260std::string GAuth::SaslClientImp::id()
const
265std::string GAuth::SaslClientImp::info()
const
270bool GAuth::SaslClientImp::validSelector( std::string_view selector )
const
272 return m_secrets.validSelector( selector ) ;
275bool GAuth::SaslClientImp::mustAuthenticate( std::string_view selector )
const
277 return m_secrets.mustAuthenticate( selector ) ;
280bool GAuth::SaslClientImp::match(
const G::StringArray & mechanisms ,
const std::string & mechanism )
282 return std::find(mechanisms.begin(),mechanisms.end(),mechanism) != mechanisms.end() ;
288 m_imp(
std::make_unique<SaslClientImp>(secrets,config))
297 return m_imp->validSelector( selector ) ;
302 return m_imp->mustAuthenticate( selector ) ;
306 std::string_view challenge , std::string_view selector )
const
308 return m_imp->response( mechanism , challenge , selector ) ;
313 return m_imp->initialResponse( selector , limit ) ;
318 return m_imp->mechanism( server_mechanisms , selector ) ;
323 return m_imp->next() ;
329 if( s.empty() )
return s ;
330 return m_imp->next() ? mechanism() : std::string() ;
336 return m_imp->mechanism() ;
346 return m_imp->info() ;
An interface used by GAuth::SaslClient to obtain a client id and its authentication secret.
std::string id() const
Returns the authentication id, valid after the last response().
SaslClient(const SaslClientSecrets &secrets, const std::string &config)
Constructor. The secrets reference is kept.
Response response(std::string_view mechanism, std::string_view challenge, std::string_view selector) const
Returns a response to the given challenge.
bool validSelector(std::string_view selector) const
Returns true if the selector is valid.
bool next()
Moves to the next preferred mechanism.
std::string info() const
Returns logging and diagnostic information, valid after the last response().
std::string mechanism() const
Returns the name of the current mechanism once next() has returned true.
Response initialResponse(std::string_view selector, std::size_t limit=0U) const
Returns an optional initial response.
bool mustAuthenticate(std::string_view selector) const
Returns true if authentication is required.
static std::string lower(std::string_view)
Returns a copy of 's' in which all seven-bit upper-case characters have been replaced by lower-case c...
static void splitIntoTokens(const std::string &in, StringArray &out, std::string_view ws, char esc='\0')
Splits the string into 'ws'-delimited tokens.
static std::string join(std::string_view sep, const StringArray &strings)
Concatenates an array of strings with separators.
static std::string upper(std::string_view)
Returns a copy of 's' in which all seven-bit lower-case characters have been replaced by upper-case c...
An interface to an underlying TLS library.
SASL authentication classes.
void keepMatch(StringArray &list, const StringArray &allow_list, Ignore=Ignore::Nothing)
Removes items in the list that do not match any entry in the allow list.
bool imatch(const StringArray &, const std::string &)
Returns true if any string in the array matches the given string, ignoring case.
void removeMatch(StringArray &list, const StringArray &deny_list, Ignore=Ignore::Nothing)
Removes items in the list that match an entry in the deny list.
std::string headMatchResidue(const StringArray &list, std::string_view head)
Returns the unmatched part of the first string in the array that has the given start.
std::vector< std::string > StringArray
A std::vector of std::strings.
Result structure returned from GAuth::SaslClient::response.