38 m_debug_name(debug_name) ,
40 m_check_time(
G::SystemTime::now())
42 m_valid = !
path.str().empty() ;
51 Contents contents = readContents( path ) ;
52 showDiagnostics( contents , path , {} , with_warnings ) ;
53 if( contents.m_errors != 0U )
63void GAuth::SecretsFile::reread()
const
68void GAuth::SecretsFile::reread(
int )
73 G_DEBUG(
"GAuth::SecretsFile::reread: file time checked at " << m_check_time <<
": now " << now ) ;
78 G_DEBUG(
"GAuth::SecretsFile::reread: current file time " << t <<
": saved file time " << m_file_time ) ;
79 if( t != m_file_time )
81 G_LOG_S(
"GAuth::Secrets: re-reading secrets file: " << m_path ) ;
88void GAuth::SecretsFile::read(
const G::Path & path )
90 m_file_time = readFileTime( path ) ;
91 m_contents = readContents( path ) ;
92 showDiagnostics( m_contents , path , m_debug_name ,
false ) ;
101GAuth::SecretsFile::Contents GAuth::SecretsFile::readContents(
const G::Path & path )
103 std::unique_ptr<std::ifstream> file ;
106 file = std::make_unique<std::ifstream>( path.
cstr() ) ;
110 throw OpenError( path.
str() ) ;
113 return readContents( *file ) ;
116GAuth::SecretsFile::Contents GAuth::SecretsFile::readContents( std::istream & file )
120 for(
unsigned int line_number = 1U ; file.good() ; ++line_number )
127 if( !line.empty() && line.at(0U) !=
'#' )
135 bool sufficient = t.valid() ;
137 bool excess = (++t).valid() ;
138 if( !w5.empty() && w5.at(0) ==
'#' )
142 addWarning( contents , line_number ,
"too many fields"_sv ) ;
145 processLine( contents , line_number , w1 , w2 , w3 , w4 , w5 ) ;
147 addError( contents , line_number ,
"too few fields"_sv ) ;
153void GAuth::SecretsFile::processLine( Contents & contents ,
unsigned int line_number ,
166 bool inserted = contents.m_trust_map.insert( {G::sv_to_string(ip_range),{G::sv_to_string(keyword),line_number}} ).second ;
168 addError( contents , line_number ,
"duplicate server trust address"_sv ) ;
170 else if( is_client_side &&
G::Str::imatch(type_in,
"plain:b") &&
id ==
"="_sv && secret ==
"="_sv )
172 contents.m_selectors.insert( {G::sv_to_string(selector),0U} ) ;
181 id_encoding =
G::Str::imatch( type_decoration ,
"b" ) ?
"base64"_sv :
"xtext"_sv ;
182 secret_encoding = id_encoding ;
187 id_encoding =
"xtext"_sv ;
188 secret_encoding =
"dotted"_sv ;
189 hash_function =
"md5"_sv ;
193 id_encoding =
"xtext"_sv ;
194 secret_encoding =
"base64"_sv ;
195 hash_function = type ;
200 std::string key = serverKey( type ,
Secret::decode({id,id_encoding}) ) ;
201 Secret secret_obj( {id,id_encoding} , {secret,secret_encoding} , hash_function , lineContext(line_number) ) ;
202 bool inserted = contents.m_map.insert( {key,secret_obj} ).second ;
206 addError( contents , line_number ,
"duplicate server secret"_sv ) ;
208 else if( is_client_side )
210 std::string key = clientKey( type , selector ) ;
211 Secret secret_obj( {id,id_encoding} , {secret,secret_encoding} , hash_function , lineContext(line_number) ) ;
212 bool inserted = contents.m_map.insert( {key,secret_obj} ).second ;
214 ((*(contents.m_selectors.insert( {G::sv_to_string(selector),0U} ).first)).second)++ ;
216 addError( contents , line_number ,
"duplicate client secret"_sv ) ;
220 addError( contents , line_number ,
"invalid value in first field"_sv , side ) ;
227 contents.m_diagnostics.emplace_back(
false , line_number , join(message,more) ) ;
232 contents.m_diagnostics.emplace_back(
true , line_number , join(message,more) ) ;
233 contents.m_errors++ ;
238 std::string message( message_in.data() , message_in.size() ) ;
244void GAuth::SecretsFile::showDiagnostics(
const Contents & c ,
const G::Path & path ,
const std::string & debug_name ,
bool with_warnings )
246 if( c.m_diagnostics.empty() )
249 if( !with_warnings && c.m_errors == 0U )
252 G_WARNING(
"GAuth::SecretsFile::read: problems reading" << (debug_name.empty()?
"":
" ") << debug_name <<
" "
253 "secrets file [" << path.
str() <<
"]..." ) ;
255 std::string prefix = path.
basename() ;
256 for(
const auto & d : c.m_diagnostics )
259 G_ERROR(
"GAuth::SecretsFile::read: " << prefix <<
"(" << std::get<1>(d) <<
"): " << std::get<2>(d) ) ;
260 else if( with_warnings )
261 G_WARNING(
"GAuth::SecretsFile::read: " << prefix <<
"(" << std::get<1>(d) <<
"): " << std::get<2>(d) ) ;
276 return serverKey( G::sv_to_string(type) , G::sv_to_string(id_decoded) ) ;
279std::string GAuth::SecretsFile::serverKey(
const std::string & type ,
const std::string & id_decoded )
281 return std::string(
"server ",7U).append(
G::Str::lower(type)).append(1U,
' ').append(id_decoded) ;
286 return std::string(
"client ",7U).append(
G::Str::lower(type)).append(selector.empty()?0U:1U,
' ').append(selector.data(),selector.size()) ;
291 return containsClientSecretImp( selector ,
false ) ;
296 return containsClientSecretImp( selector ,
true ) ;
299bool GAuth::SecretsFile::containsClientSecretImp(
G::string_view selector ,
bool with_id )
const
306 auto p = m_contents.m_selectors.find( G::sv_to_string(selector) ) ;
307 auto end = m_contents.m_selectors.end() ;
309 return p != end && (*p).second != 0U ;
321 auto p = m_contents.m_map.find( clientKey(type,selector) ) ;
322 if( p == m_contents.m_map.end() )
335 return id_decoded.empty() ?
336 m_contents.m_types.find(
G::Str::lower(type) ) != m_contents.m_types.end() :
337 m_contents.m_map.find( serverKey(type,id_decoded) ) != m_contents.m_map.end() ;
342 if( !m_valid ||
id.empty() )
347 auto p = m_contents.m_map.find( serverKey(type,
id) ) ;
348 if( p == m_contents.m_map.end() )
356 std::pair<std::string,std::string> result ;
362 auto p = m_contents.m_trust_map.find( address_range ) ;
363 if( p != m_contents.m_trust_map.end() )
365 result.first = (*p).second.first ;
366 result.second = lineContext( (*p).second.second ) ;
373 return m_path.str() ;
376std::string GAuth::SecretsFile::lineContext(
unsigned int line_number )
Encapsulates a userid/shared-secret/hash-function tuple from the secrets file.
static Secret none()
Factory function that returns a secret that is not valid().
static bool isDotted(G::string_view)
Returns true if the given secret string looks like it is in the old dotted format rather than base64.
static std::string decode(Value)
Decodes a value.
A class to read authentication secrets from file, used by GAuth::Secrets.
Secret clientSecret(G::string_view type, G::string_view selector={}) const
Returns the client id and secret for the given type.
bool containsClientSelector(G::string_view selector) const
Returns true if the given client account selector is valid.
bool containsServerSecret(G::string_view type, G::string_view id={}) const
Returns true if a server secret of the given type is available for the particular user or for any use...
static void check(const std::string &path, bool with_warnings)
Checks the given file.
bool valid() const
Returns true if the file path was supplied in the ctor.
Secret serverSecret(G::string_view type, G::string_view id) const
Returns the server secret for the given id and type.
std::string path() const
Returns the file path, as supplied to the ctor.
SecretsFile(const G::Path &path, bool auto_reread, const std::string &debug_name)
Constructor to read "client" and "server" records from the named file.
std::pair< std::string, std::string > serverTrust(const std::string &address_range) const
Returns a non-empty trustee name if the server trusts remote clients in the given address range,...
bool containsClientSecret(G::string_view selector) const
Returns true if a client secret is available with the given account selector.
static SystemTime time(const Path &file)
Returns the file's timestamp. Throws on error.
A Path object represents a file system path.
const char * cstr() const noexcept
Returns the path string.
std::string basename() const
Returns the rightmost part of the path, ignoring "." parts.
std::string str() const
Returns the path string.
A class which acquires the process's special privileges on construction and releases them on destruct...
static string_view headView(string_view in, std::size_t pos, string_view default_={}) noexcept
Like head() but returning a view into the input string.
static string_view tailView(string_view in, std::size_t pos, string_view default_={}) noexcept
Like tail() but returning a view into the input string.
static std::istream & readLine(std::istream &stream, std::string &result, string_view eol={}, bool pre_erase_result=true, std::size_t limit=0U)
Reads a line from the stream using the given line terminator, which may be multi-character.
static bool imatch(char, char) noexcept
Returns true if the two characters are the same, ignoring seven-bit case.
static std::string lower(string_view)
Returns a copy of 's' in which all seven-bit upper-case characters have been replaced by lower-case c...
static std::string fromUInt(unsigned int ui)
Converts unsigned int 'ui' to a string.
static std::string printable(const std::string &in, char escape='\\')
Returns a printable representation of the given input string, using chacter code ranges 0x20 to 0x7e ...
static string_view ws() noexcept
Returns a string of standard whitespace characters.
static std::string & trim(std::string &s, string_view ws)
Trims both ends of s, taking off any of the 'ws' characters.
A zero-copy string token iterator where the token separators are runs of whitespace characters,...
Represents a unix-epoch time with microsecond resolution.
static SystemTime now()
Factory function for the current time.
bool sameSecond(const SystemTime &other) const noexcept
Returns true if this time and the other time are the same, at second resolution.
A class like c++17's std::string_view.