39 m_debug_name(debug_name) ,
41 m_check_time(
G::SystemTime::now())
43 m_valid = !
path.str().empty() ;
52 Contents contents = readContents( path ) ;
53 showDiagnostics( contents , path , {} , with_warnings ) ;
54 if( contents.m_errors != 0U )
64void GAuth::SecretsFile::reread()
const
69void GAuth::SecretsFile::reread(
int )
74 G_DEBUG(
"GAuth::SecretsFile::reread: file time checked at " << m_check_time <<
": now " << now ) ;
79 G_DEBUG(
"GAuth::SecretsFile::reread: current file time " << t <<
": saved file time " << m_file_time ) ;
80 if( t != m_file_time )
82 G_LOG_S(
"GAuth::Secrets: re-reading secrets file: " << m_path ) ;
89void GAuth::SecretsFile::read(
const G::Path & path )
91 m_file_time = readFileTime( path ) ;
92 m_contents = readContents( path ) ;
93 showDiagnostics( m_contents , path , m_debug_name ,
false ) ;
102GAuth::SecretsFile::Contents GAuth::SecretsFile::readContents(
const G::Path & path )
104 std::unique_ptr<std::ifstream> file ;
107 file = std::make_unique<std::ifstream>( path.
iopath() ) ;
111 throw OpenError( path.
str() ) ;
114 return readContents( *file ) ;
117GAuth::SecretsFile::Contents GAuth::SecretsFile::readContents( std::istream & file )
121 for(
unsigned int line_number = 1U ; file.good() ; ++line_number )
128 if( !line.empty() && line.at(0U) !=
'#' )
130 std::string_view line_sv( line ) ;
132 std::string_view w1 = t() ;
133 std::string_view w2 = (++t)() ;
134 std::string_view w3 = (++t)() ;
135 std::string_view w4 = (++t)() ;
136 bool sufficient = t.valid() ;
137 std::string_view w5 = (++t)() ;
138 bool excess = (++t).valid() ;
139 if( !w5.empty() && w5.at(0) ==
'#' )
140 w5 = std::string_view() , excess = false ;
143 addWarning( contents , line_number ,
"too many fields"_sv ) ;
146 processLine( contents , line_number , w1 , w2 , w3 , w4 , w5 ) ;
148 addError( contents , line_number ,
"too few fields"_sv ) ;
154void GAuth::SecretsFile::processLine( Contents & contents ,
unsigned int line_number ,
155 std::string_view side , std::string_view type_in , std::string_view
id ,
156 std::string_view secret , std::string_view selector )
158 std::string_view type = canonicalView(
G::Str::headView( type_in ,
":" ,
false ) ) ;
165 std::string_view ip_range = id ;
166 std::string_view keyword = secret ;
167 bool inserted = contents.m_trust_map.insert( {G::sv_to_string(ip_range),{G::sv_to_string(keyword),line_number}} ).second ;
169 addError( contents , line_number ,
"duplicate server trust address"_sv ) ;
171 else if( is_client_side &&
G::Str::imatch(type_in,
"plain:b") &&
id ==
"="_sv && secret ==
"="_sv )
173 contents.m_selectors.insert( {G::sv_to_string(selector),0U} ) ;
177 std::string_view id_encoding ;
178 std::string_view secret_encoding ;
179 std::string_view hash_function ;
182 id_encoding =
G::Str::imatch( type_decoration ,
"b" ) ?
"base64"_sv :
"xtext"_sv ;
183 secret_encoding = id_encoding ;
188 id_encoding =
"xtext"_sv ;
189 secret_encoding =
"dotted"_sv ;
190 hash_function =
"md5"_sv ;
194 id_encoding =
"xtext"_sv ;
195 secret_encoding =
"base64"_sv ;
196 hash_function = type ;
201 std::string key = serverKey( type ,
Secret::decode({id,id_encoding}) ) ;
202 Secret secret_obj( {id,id_encoding} , {secret,secret_encoding} , hash_function , lineContext(line_number) ) ;
203 bool inserted = contents.m_map.insert( {key,secret_obj} ).second ;
207 addError( contents , line_number ,
"duplicate server secret"_sv ) ;
209 else if( is_client_side )
211 std::string key = clientKey( type , selector ) ;
212 Secret secret_obj( {id,id_encoding} , {secret,secret_encoding} , hash_function , lineContext(line_number) ) ;
213 bool inserted = contents.m_map.insert( {key,secret_obj} ).second ;
215 ((*(contents.m_selectors.insert( {G::sv_to_string(selector),0U} ).first)).second)++ ;
217 addError( contents , line_number ,
"duplicate client secret"_sv ) ;
221 addError( contents , line_number ,
"invalid value in first field"_sv , side ) ;
226void GAuth::SecretsFile::addWarning( Contents & contents ,
unsigned int line_number , std::string_view message , std::string_view more )
228 contents.m_diagnostics.emplace_back(
false , line_number , join(message,more) ) ;
231void GAuth::SecretsFile::addError( Contents & contents ,
unsigned int line_number , std::string_view message , std::string_view more )
233 contents.m_diagnostics.emplace_back(
true , line_number , join(message,more) ) ;
234 contents.m_errors++ ;
237std::string GAuth::SecretsFile::join( std::string_view message_in , std::string_view more )
239 std::string message( message_in.data() , message_in.size() ) ;
245void GAuth::SecretsFile::showDiagnostics(
const Contents & c ,
const G::Path & path ,
const std::string & debug_name ,
bool with_warnings )
247 if( c.m_diagnostics.empty() )
250 if( !with_warnings && c.m_errors == 0U )
253 G_WARNING(
"GAuth::SecretsFile::read: problems reading" << (debug_name.empty()?
"":
" ") << debug_name <<
" "
254 "secrets file [" << path.
str() <<
"]..." ) ;
256 std::string prefix = path.
basename() ;
257 for(
const auto & d : c.m_diagnostics )
260 G_ERROR(
"GAuth::SecretsFile::read: " << prefix <<
"(" << std::get<1>(d) <<
"): " << std::get<2>(d) ) ;
261 else if( with_warnings )
262 G_WARNING(
"GAuth::SecretsFile::read: " << prefix <<
"(" << std::get<1>(d) <<
"): " << std::get<2>(d) ) ;
266std::string_view GAuth::SecretsFile::canonicalView( std::string_view type )
275std::string GAuth::SecretsFile::serverKey( std::string_view type , std::string_view id_decoded )
277 return serverKey( G::sv_to_string(type) , G::sv_to_string(id_decoded) ) ;
280std::string GAuth::SecretsFile::serverKey(
const std::string & type ,
const std::string & id_decoded )
282 return std::string(
"server ",7U).append(
G::Str::lower(type)).append(1U,
' ').append(id_decoded) ;
285std::string GAuth::SecretsFile::clientKey( std::string_view type , std::string_view selector )
287 return std::string(
"client ",7U).append(
G::Str::lower(type)).append(selector.empty()?0U:1U,
' ').append(selector.data(),selector.size()) ;
292 return containsClientSecretImp( selector ,
false ) ;
297 return containsClientSecretImp( selector ,
true ) ;
300bool GAuth::SecretsFile::containsClientSecretImp( std::string_view selector ,
bool with_id )
const
307 auto p = m_contents.m_selectors.find( G::sv_to_string(selector) ) ;
308 auto end = m_contents.m_selectors.end() ;
310 return p != end && (*p).second != 0U ;
322 auto p = m_contents.m_map.find( clientKey(type,selector) ) ;
323 if( p == m_contents.m_map.end() )
336 return id_decoded.empty() ?
337 m_contents.m_server_types.find(
G::Str::lower(type) ) != m_contents.m_server_types.end() :
338 m_contents.m_map.find( serverKey(type,id_decoded) ) != m_contents.m_map.end() ;
343 if( !m_valid ||
id.empty() )
348 auto p = m_contents.m_map.find( serverKey(type,
id) ) ;
349 if( p == m_contents.m_map.end() )
357 std::pair<std::string,std::string> result ;
363 auto p = m_contents.m_trust_map.find( address_range ) ;
364 if( p != m_contents.m_trust_map.end() )
366 result.first = (*p).second.first ;
367 result.second = lineContext( (*p).second.second ) ;
374 return m_path.str() ;
377std::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(std::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 serverSecret(std::string_view type, std::string_view id) const
Returns the server secret for the given id and type.
bool containsClientSecret(std::string_view selector) const
Returns true if a client secret is available with the given account selector.
static void check(const std::string &path, bool with_warnings)
Checks the given file.
bool containsServerSecret(std::string_view type, std::string_view id={}) const
Returns true if a server secret of the given type is available for the particular user or for any use...
Secret clientSecret(std::string_view type, std::string_view selector={}) const
Returns the client id and secret for the given type.
bool valid() const
Returns true if the file path was supplied in the ctor.
std::string path() const
Returns the file path, as supplied to the ctor.
bool containsClientSelector(std::string_view selector) const
Returns true if the given client account selector is valid.
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,...
static SystemTime time(const Path &file)
Returns the file's timestamp. Throws on error.
A Path object represents a file system path.
std::string basename() const
Returns the rightmost part of the path, ignoring "." parts.
std::string str() const
Returns the path string.
const iopath_char_type * iopath() const
Returns the path's string with a type that is suitable for initialising std::fstreams.
A class which acquires the process's special privileges on construction and releases them on destruct...
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 bool imatch(char, char) noexcept
Returns true if the two characters are the same, ignoring seven-bit case.
static std::istream & readLine(std::istream &stream, std::string &result, std::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 std::string & trim(std::string &s, std::string_view ws)
Trims both ends of s, taking off any of the 'ws' characters.
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 std::string_view tailView(std::string_view in, std::size_t pos, std::string_view default_={}) noexcept
Like tail() but returning a view into the input string.
static std::string_view ws() noexcept
Returns a string of standard whitespace characters.
static std::string_view headView(std::string_view in, std::size_t pos, std::string_view default_={}) noexcept
Like head() but returning a view into the input string.
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.