42 m_debug_name(debug_name) ,
44 m_check_time(
G::SystemTime::now())
46 m_valid = !
path.str().empty() ;
55 Contents contents = readContents( path ) ;
56 showWarnings( contents.m_warnings , path ) ;
57 if( !contents.m_warnings.empty() )
67void GAuth::SecretsFile::reread()
const
72void GAuth::SecretsFile::reread(
int )
77 G_DEBUG(
"GAuth::SecretsFile::reread: file time checked at " << m_check_time <<
": now " << now ) ;
82 G_DEBUG(
"GAuth::SecretsFile::reread: current file time " << t <<
": saved file time " << m_file_time ) ;
83 if( t != m_file_time )
85 G_LOG_S(
"GAuth::Secrets: re-reading secrets file: " << m_path ) ;
92void GAuth::SecretsFile::read(
const G::Path & path )
94 m_file_time = readFileTime( path ) ;
95 m_contents = readContents( path ) ;
96 showWarnings( m_contents.m_warnings , path , m_debug_name ) ;
105GAuth::SecretsFile::Contents GAuth::SecretsFile::readContents(
const G::Path & path )
107 std::unique_ptr<std::ifstream> file ;
110 file = std::make_unique<std::ifstream>( path.
cstr() ) ;
114 throw Secrets::OpenError( path.
str() ) ;
117 return readContents( *file ) ;
120GAuth::SecretsFile::Contents GAuth::SecretsFile::readContents( std::istream & file )
123 for(
unsigned int line_number = 1U ; file.good() ; ++line_number )
130 if( !line.empty() && line.at(0U) !=
'#' )
134 if( word_array.size() >= 4U )
137 processLine( contents , line_number , word_array[0U] , word_array[1U] , word_array[2U] , word_array[3U] ) ;
141 addWarning( contents , line_number ,
"too few fields" ) ;
148void GAuth::SecretsFile::processLine( Contents & contents ,
149 unsigned int line_number ,
const std::string & side ,
const std::string & type_in ,
150 const std::string & id_or_ip_in ,
const std::string & secret_in )
153 std::string id_or_ip = id_or_ip_in ;
154 std::string secret = secret_in ;
155 if( type ==
"plain:b" )
161 addWarning( contents , line_number ,
"invalid base64 encoding in third field" , id_or_ip ) ;
163 addWarning( contents , line_number ,
"invalid base64 encoding in fourth field" ) ;
164 if( !valid_id || !valid_secret )
170 processLineImp( contents , line_number ,
G::Str::lower(side) , type , id_or_ip , secret ) ;
173void GAuth::SecretsFile::processLineImp( Contents & contents ,
174 unsigned int line_number ,
const std::string & side ,
const std::string & type ,
175 const std::string & id_or_ip ,
const std::string & secret )
177 G_ASSERT( !side.empty() && !type.empty() && !id_or_ip.empty() && !secret.empty() ) ;
179 if( type ==
"plain" )
182 addWarning( contents , line_number ,
"invalid xtext encoding in third field" , id_or_ip ) ;
184 addWarning( contents , line_number ,
"invalid xtext encoding in fourth field" ) ;
187 if( side ==
"server" )
190 std::string key = serverKey( type , id_or_ip ) ;
191 Value value( secret , line_number ) ;
192 bool inserted = contents.m_map.insert(std::make_pair(key,value)).second ;
194 contents.m_types.insert( canonical(type) ) ;
196 addWarning( contents , line_number ,
"duplicate server secret" , key ) ;
198 else if( side ==
"client" )
201 const std::string &
id = id_or_ip ;
202 std::string key = clientKey( type ) ;
203 Value value(
id +
" " + secret , line_number ) ;
204 bool inserted = contents.m_map.insert(std::make_pair(key,value)).second ;
206 addWarning( contents , line_number ,
"too many client secrets" , key ) ;
210 addWarning( contents , line_number ,
"invalid value in first field" , side ) ;
214void GAuth::SecretsFile::addWarning( Contents & contents ,
unsigned int line_number ,
const std::string & message_in ,
const std::string & more )
216 std::string message = more.empty() ? message_in : ( message_in +
": [" +
G::Str::printable(more) +
"]" ) ;
217 contents.m_warnings.push_back( Warnings::value_type(line_number,message) ) ;
220void GAuth::SecretsFile::showWarnings(
const Warnings & warnings ,
const G::Path & path ,
const std::string & debug_name )
222 if( !warnings.empty() )
224 std::string prefix = path.
basename() ;
225 G_WARNING(
"GAuth::SecretsFile::read: problems reading" << (debug_name.empty()?
"":
" ") << debug_name <<
" secrets file [" << path.
str() <<
"]..." ) ;
226 for(
const auto & warning : warnings )
228 G_WARNING(
"GAuth::SecretsFile::read: " << prefix <<
"(" << warning.first <<
"): " << warning.second ) ;
233std::string GAuth::SecretsFile::canonical(
const std::string & type_in )
238 if( type ==
"cram-md5" ) type =
"md5" ;
239 if( type ==
"apop" ) type =
"md5" ;
240 if( type ==
"login" ) type =
"plain" ;
244std::string GAuth::SecretsFile::serverKey(
const std::string & type ,
const std::string & id_xtext )
251 return "server " + canonical(type) +
" " + id_xtext ;
254std::string GAuth::SecretsFile::clientKey(
const std::string & type )
259 return "client " + canonical(type) ;
266 auto p = m_contents.m_map.find( clientKey(type) ) ;
267 if( p == m_contents.m_map.end() )
273 std::string id_xtext =
G::Str::head( (*p).second.s ,
" " ) ;
274 std::string secret_encoded =
G::Str::tail( (*p).second.s ,
" " ) ;
275 return Secret( secret_encoded , canonical(type) , id_xtext ,
true , line((*p).second.n) ) ;
287 if( p == m_contents.m_map.end() )
293 return Secret( (*p).second.s , canonical(type) ,
id ,
false , line((*p).second.n) ) ;
301 std::pair<std::string,std::string> result ;
302 std::string type =
"none" ;
303 const std::string &
id = address_range ;
305 if( p != m_contents.m_map.end() )
307 result.first = (*p).second.s ;
308 result.second = line( (*p).second.n ) ;
315 return m_path.str() ;
321 m_contents.m_types.find( canonical(type) ) != m_contents.m_types.end() :
322 m_contents.m_map.find( serverKey(type,
G::Xtext::encode(
id)) ) != m_contents.m_map.end() ;
325std::string GAuth::SecretsFile::line(
unsigned int line_number )
Encapsulates a shared secret from the secrets file plus the associated userid.
static Secret none()
Factory function that returns a secret that is not valid() and has an empty id().
A class to read authentication secrets from file, used by GAuth::Secrets.
Secret serverSecret(const std::string &type, const std::string &id) const
Returns the server secret for the given id and type.
static void check(const std::string &path)
Checks the given file.
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 contains(const std::string &type, const std::string &id={}) const
Returns true if a server secret of the given type is available for the particular user or any user.
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 clients in the given address range,...
Secret clientSecret(const std::string &type) const
Returns the client id and secret for the given type.
static std::string decode(const std::string &, bool throw_on_invalid=false, bool strict=true)
Decodes the given string.
static bool valid(const std::string &, bool strict=true)
Returns true if the string is a valid base64 encoding, possibly allowing for embedded newlines,...
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 ws()
Returns a string of standard whitespace characters.
static std::string tail(const std::string &in, std::size_t pos, const std::string &default_=std::string())
Returns the last part of the string after the given position.
static void splitIntoTokens(const std::string &in, StringArray &out, string_view ws, char esc='\0')
Splits the string into 'ws'-delimited tokens.
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 head(const std::string &in, std::size_t pos, const std::string &default_=std::string())
Returns the first part of the string up to just before the given position.
static std::string lower(const std::string &s)
Returns a copy of 's' in which all Latin-1 upper-case characters have been replaced by lower-case cha...
static std::string readLineFrom(std::istream &stream, const std::string &eol=std::string())
Reads a line from the stream using the given line terminator.
static std::string & trim(std::string &s, string_view ws)
Trims both ends of s, taking off any of the 'ws' 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.
static bool valid(const std::string &, bool strict=false)
Returns true if a valid encoding.
static std::string encode(const std::string &)
Encodes the given string.
std::vector< std::string > StringArray
A std::vector of std::strings.