2019-11-09 15:33:07 +00:00
# include <Access/UsersConfigAccessStorage.h>
2019-11-04 19:17:27 +00:00
# include <Access/Quota.h>
2019-11-17 11:57:02 +00:00
# include <Access/RowPolicy.h>
2020-01-26 09:49:53 +00:00
# include <Access/User.h>
2020-03-04 22:27:03 +00:00
# include <Access/SettingsProfile.h>
2022-05-06 23:37:23 +00:00
# include <Access/AccessControl.h>
2022-05-16 18:43:55 +00:00
# include <Access/AccessChangesNotifier.h>
2020-01-26 09:49:53 +00:00
# include <Dictionaries/IDictionary.h>
2020-08-05 19:54:06 +00:00
# include <Common/Config/ConfigReloader.h>
2019-11-04 19:17:27 +00:00
# include <Common/StringUtils/StringUtils.h>
# include <Common/quoteString.h>
2020-07-19 14:59:07 +00:00
# include <Core/Settings.h>
2019-11-09 15:33:07 +00:00
# include <Poco/Util/AbstractConfiguration.h>
# include <Poco/MD5Engine.h>
2020-09-14 22:51:53 +00:00
# include <Poco/JSON/JSON.h>
# include <Poco/JSON/Object.h>
# include <Poco/JSON/Stringifier.h>
2019-11-09 15:33:07 +00:00
# include <cstring>
2020-08-05 19:54:06 +00:00
# include <filesystem>
2021-10-02 19:47:35 +00:00
# include <base/FnTraits.h>
2019-11-09 15:33:07 +00:00
namespace DB
{
2020-01-26 09:49:53 +00:00
namespace ErrorCodes
{
extern const int BAD_ARGUMENTS ;
extern const int UNKNOWN_ADDRESS_PATTERN_TYPE ;
2020-03-04 22:27:03 +00:00
extern const int NOT_IMPLEMENTED ;
2022-02-18 20:58:46 +00:00
}
2020-01-26 09:49:53 +00:00
2019-11-09 15:33:07 +00:00
namespace
{
2022-02-28 18:51:49 +00:00
2021-11-18 20:54:18 +00:00
UUID generateID ( AccessEntityType type , const String & name )
2019-11-09 15:33:07 +00:00
{
Poco : : MD5Engine md5 ;
md5 . update ( name ) ;
char type_storage_chars [ ] = " USRSXML " ;
2021-11-18 20:54:18 +00:00
type_storage_chars [ 0 ] = AccessEntityTypeInfo : : get ( type ) . unique_char ;
2019-11-09 15:33:07 +00:00
md5 . update ( type_storage_chars , strlen ( type_storage_chars ) ) ;
UUID result ;
memcpy ( & result , md5 . digest ( ) . data ( ) , md5 . digestLength ( ) ) ;
return result ;
}
2020-05-02 22:30:28 +00:00
UUID generateID ( const IAccessEntity & entity ) { return generateID ( entity . getType ( ) , entity . getName ( ) ) ; }
2019-11-04 19:17:27 +00:00
2020-05-03 03:12:03 +00:00
2022-03-14 14:32:58 +00:00
UserPtr parseUser ( const Poco : : Util : : AbstractConfiguration & config , const String & user_name , bool allow_no_password , bool allow_plaintext_password )
2020-01-26 09:49:53 +00:00
{
auto user = std : : make_shared < User > ( ) ;
user - > setName ( user_name ) ;
String user_config = " users. " + user_name ;
2020-05-20 13:08:43 +00:00
bool has_no_password = config . has ( user_config + " .no_password " ) ;
bool has_password_plaintext = config . has ( user_config + " .password " ) ;
2020-01-26 09:49:53 +00:00
bool has_password_sha256_hex = config . has ( user_config + " .password_sha256_hex " ) ;
bool has_password_double_sha1_hex = config . has ( user_config + " .password_double_sha1_hex " ) ;
2020-05-27 21:06:33 +00:00
bool has_ldap = config . has ( user_config + " .ldap " ) ;
2021-03-11 20:41:10 +00:00
bool has_kerberos = config . has ( user_config + " .kerberos " ) ;
2020-01-26 09:49:53 +00:00
2022-02-18 20:58:58 +00:00
const auto certificates_config = user_config + " .ssl_certificates " ;
2022-01-11 14:07:30 +00:00
bool has_certificates = config . has ( certificates_config ) ;
size_t num_password_fields = has_no_password + has_password_plaintext + has_password_sha256_hex + has_password_double_sha1_hex + has_ldap + has_kerberos + has_certificates ;
2022-02-28 19:12:54 +00:00
2020-05-20 13:08:43 +00:00
if ( num_password_fields > 1 )
2022-05-11 16:56:12 +00:00
throw Exception ( " More than one field of 'password', 'password_sha256_hex', 'password_double_sha1_hex', 'no_password', 'ldap', 'kerberos', 'ssl_certificates' are used to specify authentication info for user " + user_name + " . Must be only one of them. " ,
2020-01-26 09:49:53 +00:00
ErrorCodes : : BAD_ARGUMENTS ) ;
2020-05-20 13:08:43 +00:00
if ( num_password_fields < 1 )
2022-05-11 16:56:12 +00:00
throw Exception ( " Either 'password' or 'password_sha256_hex' or 'password_double_sha1_hex' or 'no_password' or 'ldap' or 'kerberos' or 'ssl_certificates' must be specified for user " + user_name + " . " , ErrorCodes : : BAD_ARGUMENTS ) ;
2020-01-26 09:49:53 +00:00
2022-02-18 20:58:46 +00:00
if ( has_password_plaintext )
2020-01-26 09:49:53 +00:00
{
2021-11-01 14:03:20 +00:00
user - > auth_data = AuthenticationData { AuthenticationType : : PLAINTEXT_PASSWORD } ;
user - > auth_data . setPassword ( config . getString ( user_config + " .password " ) ) ;
2020-01-26 09:49:53 +00:00
}
else if ( has_password_sha256_hex )
{
2021-11-01 14:03:20 +00:00
user - > auth_data = AuthenticationData { AuthenticationType : : SHA256_PASSWORD } ;
user - > auth_data . setPasswordHashHex ( config . getString ( user_config + " .password_sha256_hex " ) ) ;
2020-01-26 09:49:53 +00:00
}
else if ( has_password_double_sha1_hex )
{
2021-11-01 14:03:20 +00:00
user - > auth_data = AuthenticationData { AuthenticationType : : DOUBLE_SHA1_PASSWORD } ;
user - > auth_data . setPasswordHashHex ( config . getString ( user_config + " .password_double_sha1_hex " ) ) ;
2020-01-26 09:49:53 +00:00
}
2020-05-27 21:06:33 +00:00
else if ( has_ldap )
{
bool has_ldap_server = config . has ( user_config + " .ldap.server " ) ;
if ( ! has_ldap_server )
throw Exception ( " Missing mandatory 'server' in 'ldap', with LDAP server name, for user " + user_name + " . " , ErrorCodes : : BAD_ARGUMENTS ) ;
const auto ldap_server_name = config . getString ( user_config + " .ldap.server " ) ;
2020-06-03 18:52:12 +00:00
if ( ldap_server_name . empty ( ) )
throw Exception ( " LDAP server name cannot be empty for user " + user_name + " . " , ErrorCodes : : BAD_ARGUMENTS ) ;
2020-05-27 21:06:33 +00:00
2021-11-01 14:03:20 +00:00
user - > auth_data = AuthenticationData { AuthenticationType : : LDAP } ;
user - > auth_data . setLDAPServerName ( ldap_server_name ) ;
2021-03-11 20:41:10 +00:00
}
else if ( has_kerberos )
{
const auto realm = config . getString ( user_config + " .kerberos.realm " , " " ) ;
2021-11-01 14:03:20 +00:00
user - > auth_data = AuthenticationData { AuthenticationType : : KERBEROS } ;
user - > auth_data . setKerberosRealm ( realm ) ;
2020-05-27 21:06:33 +00:00
}
2022-01-11 14:07:30 +00:00
else if ( has_certificates )
{
user - > auth_data = AuthenticationData { AuthenticationType : : SSL_CERTIFICATE } ;
/// Fill list of allowed certificates.
Poco : : Util : : AbstractConfiguration : : Keys keys ;
config . keys ( certificates_config , keys ) ;
2022-02-18 18:01:30 +00:00
boost : : container : : flat_set < String > common_names ;
2022-01-11 14:07:30 +00:00
for ( const String & key : keys )
{
if ( key . starts_with ( " common_name " ) )
{
String value = config . getString ( certificates_config + " . " + key ) ;
2022-02-18 18:01:30 +00:00
common_names . insert ( std : : move ( value ) ) ;
2022-01-11 14:07:30 +00:00
}
else
2022-02-18 18:01:30 +00:00
throw Exception ( " Unknown certificate pattern type: " + key , ErrorCodes : : BAD_ARGUMENTS ) ;
}
user - > auth_data . setSSLCertificateCommonNames ( std : : move ( common_names ) ) ;
2022-01-11 14:07:30 +00:00
}
2020-01-26 09:49:53 +00:00
2022-03-14 14:32:58 +00:00
auto auth_type = user - > auth_data . getType ( ) ;
if ( ( ( auth_type = = AuthenticationType : : NO_PASSWORD ) & & ! allow_no_password ) | |
( ( auth_type = = AuthenticationType : : PLAINTEXT_PASSWORD ) & & ! allow_plaintext_password ) )
{
throw Exception ( ErrorCodes : : BAD_ARGUMENTS ,
" Authentication type {} is not allowed, check the setting allow_{} in the server configuration " ,
toString ( auth_type ) , AuthenticationTypeInfo : : get ( auth_type ) . name ) ;
}
2020-03-04 22:27:03 +00:00
const auto profile_name_config = user_config + " .profile " ;
if ( config . has ( profile_name_config ) )
{
auto profile_name = config . getString ( profile_name_config ) ;
SettingsProfileElement profile_element ;
2021-11-18 20:54:18 +00:00
profile_element . parent_profile = generateID ( AccessEntityType : : SETTINGS_PROFILE , profile_name ) ;
2020-03-04 22:27:03 +00:00
user - > settings . push_back ( std : : move ( profile_element ) ) ;
}
2020-01-26 09:49:53 +00:00
/// Fill list of allowed hosts.
const auto networks_config = user_config + " .networks " ;
if ( config . has ( networks_config ) )
{
Poco : : Util : : AbstractConfiguration : : Keys keys ;
config . keys ( networks_config , keys ) ;
2020-02-02 00:48:11 +00:00
user - > allowed_client_hosts . clear ( ) ;
2020-01-26 09:49:53 +00:00
for ( const String & key : keys )
{
String value = config . getString ( networks_config + " . " + key ) ;
if ( key . starts_with ( " ip " ) )
user - > allowed_client_hosts . addSubnet ( value ) ;
else if ( key . starts_with ( " host_regexp " ) )
2020-02-02 00:48:11 +00:00
user - > allowed_client_hosts . addNameRegexp ( value ) ;
2020-01-26 09:49:53 +00:00
else if ( key . starts_with ( " host " ) )
2020-02-02 00:48:11 +00:00
user - > allowed_client_hosts . addName ( value ) ;
2020-01-26 09:49:53 +00:00
else
throw Exception ( " Unknown address pattern type: " + key , ErrorCodes : : UNKNOWN_ADDRESS_PATTERN_TYPE ) ;
}
}
/// Fill list of allowed databases.
const auto databases_config = user_config + " .allow_databases " ;
std : : optional < Strings > databases ;
if ( config . has ( databases_config ) )
{
Poco : : Util : : AbstractConfiguration : : Keys keys ;
config . keys ( databases_config , keys ) ;
databases . emplace ( ) ;
databases - > reserve ( keys . size ( ) ) ;
for ( const auto & key : keys )
{
const auto database_name = config . getString ( databases_config + " . " + key ) ;
databases - > push_back ( database_name ) ;
}
}
/// Fill list of allowed dictionaries.
const auto dictionaries_config = user_config + " .allow_dictionaries " ;
std : : optional < Strings > dictionaries ;
if ( config . has ( dictionaries_config ) )
{
Poco : : Util : : AbstractConfiguration : : Keys keys ;
config . keys ( dictionaries_config , keys ) ;
dictionaries . emplace ( ) ;
dictionaries - > reserve ( keys . size ( ) ) ;
for ( const auto & key : keys )
{
const auto dictionary_name = config . getString ( dictionaries_config + " . " + key ) ;
dictionaries - > push_back ( dictionary_name ) ;
}
}
2020-04-20 22:07:00 +00:00
/// By default all databases are accessible
/// and the user can grant everything he has.
user - > access . grantWithGrantOption ( AccessType : : ALL ) ;
2020-01-26 09:49:53 +00:00
if ( databases )
{
2020-03-06 14:36:01 +00:00
user - > access . revoke ( AccessFlags : : allFlags ( ) - AccessFlags : : allGlobalFlags ( ) ) ;
2020-04-20 22:07:00 +00:00
user - > access . grantWithGrantOption ( AccessFlags : : allDictionaryFlags ( ) , IDictionary : : NO_DATABASE_TAG ) ;
2020-01-26 09:49:53 +00:00
for ( const String & database : * databases )
2020-04-20 22:07:00 +00:00
user - > access . grantWithGrantOption ( AccessFlags : : allFlags ( ) , database ) ;
2020-01-26 09:49:53 +00:00
}
if ( dictionaries )
{
2020-03-06 14:36:01 +00:00
user - > access . revoke ( AccessFlags : : allDictionaryFlags ( ) , IDictionary : : NO_DATABASE_TAG ) ;
2020-01-26 09:49:53 +00:00
for ( const String & dictionary : * dictionaries )
2020-04-20 22:07:00 +00:00
user - > access . grantWithGrantOption ( AccessFlags : : allDictionaryFlags ( ) , IDictionary : : NO_DATABASE_TAG , dictionary ) ;
2020-01-26 09:49:53 +00:00
}
2020-04-05 16:28:52 +00:00
bool access_management = config . getBool ( user_config + " .access_management " , false ) ;
if ( ! access_management )
{
user - > access . revoke ( AccessType : : ACCESS_MANAGEMENT ) ;
2020-04-20 22:07:00 +00:00
user - > access . revokeGrantOption ( AccessType : : ALL ) ;
2020-04-05 16:28:52 +00:00
}
2020-02-04 22:31:04 +00:00
2021-07-05 16:55:07 +00:00
String default_database = config . getString ( user_config + " .default_database " , " " ) ;
2021-07-18 06:56:26 +00:00
user - > default_database = default_database ;
2021-07-05 16:55:07 +00:00
2020-01-26 09:49:53 +00:00
return user ;
}
2022-03-14 14:32:58 +00:00
std : : vector < AccessEntityPtr > parseUsers ( const Poco : : Util : : AbstractConfiguration & config , bool allow_no_password , bool allow_plaintext_password )
2020-01-26 09:49:53 +00:00
{
Poco : : Util : : AbstractConfiguration : : Keys user_names ;
config . keys ( " users " , user_names ) ;
std : : vector < AccessEntityPtr > users ;
users . reserve ( user_names . size ( ) ) ;
for ( const auto & user_name : user_names )
2021-05-21 08:25:43 +00:00
{
try
{
2022-03-14 14:32:58 +00:00
users . push_back ( parseUser ( config , user_name , allow_no_password , allow_plaintext_password ) ) ;
2021-05-21 08:25:43 +00:00
}
catch ( Exception & e )
{
e . addMessage ( fmt : : format ( " while parsing user '{}' in users configuration file " , user_name ) ) ;
throw ;
}
}
2020-09-03 16:52:08 +00:00
2020-01-26 09:49:53 +00:00
return users ;
}
2020-02-10 02:26:56 +00:00
QuotaPtr parseQuota ( const Poco : : Util : : AbstractConfiguration & config , const String & quota_name , const std : : vector < UUID > & user_ids )
2019-11-04 19:17:27 +00:00
{
auto quota = std : : make_shared < Quota > ( ) ;
quota - > setName ( quota_name ) ;
String quota_config = " quotas. " + quota_name ;
if ( config . has ( quota_config + " .keyed_by_ip " ) )
2021-11-18 18:43:02 +00:00
quota - > key_type = QuotaKeyType : : IP_ADDRESS ;
2020-12-01 21:07:20 +00:00
else if ( config . has ( quota_config + " .keyed_by_forwarded_ip " ) )
2021-11-18 18:43:02 +00:00
quota - > key_type = QuotaKeyType : : FORWARDED_IP_ADDRESS ;
2019-11-04 19:17:27 +00:00
else if ( config . has ( quota_config + " .keyed " ) )
2021-11-18 18:43:02 +00:00
quota - > key_type = QuotaKeyType : : CLIENT_KEY_OR_USER_NAME ;
2019-11-04 19:17:27 +00:00
else
2021-11-18 18:43:02 +00:00
quota - > key_type = QuotaKeyType : : USER_NAME ;
2019-11-04 19:17:27 +00:00
Poco : : Util : : AbstractConfiguration : : Keys interval_keys ;
config . keys ( quota_config , interval_keys ) ;
for ( const String & interval_key : interval_keys )
{
if ( ! startsWith ( interval_key , " interval " ) )
continue ;
String interval_config = quota_config + " . " + interval_key ;
std : : chrono : : seconds duration { config . getInt ( interval_config + " .duration " , 0 ) } ;
if ( duration . count ( ) < = 0 ) /// Skip quotas with non-positive duration.
continue ;
quota - > all_limits . emplace_back ( ) ;
auto & limits = quota - > all_limits . back ( ) ;
limits . duration = duration ;
limits . randomize_interval = config . getBool ( interval_config + " .randomize " , false ) ;
2021-11-18 18:07:35 +00:00
for ( auto quota_type : collections : : range ( QuotaType : : MAX ) )
2020-05-08 12:50:45 +00:00
{
2021-11-18 18:07:35 +00:00
const auto & type_info = QuotaTypeInfo : : get ( quota_type ) ;
2020-05-08 12:50:45 +00:00
auto value = config . getString ( interval_config + " . " + type_info . name , " 0 " ) ;
if ( value ! = " 0 " )
2021-11-18 18:07:35 +00:00
limits . max [ static_cast < size_t > ( quota_type ) ] = type_info . stringToValue ( value ) ;
2020-05-08 12:50:45 +00:00
}
2019-11-04 19:17:27 +00:00
}
2020-03-07 17:37:38 +00:00
quota - > to_roles . add ( user_ids ) ;
2019-11-04 19:17:27 +00:00
return quota ;
}
2020-09-03 16:52:08 +00:00
std : : vector < AccessEntityPtr > parseQuotas ( const Poco : : Util : : AbstractConfiguration & config )
2019-11-04 19:17:27 +00:00
{
Poco : : Util : : AbstractConfiguration : : Keys user_names ;
config . keys ( " users " , user_names ) ;
2020-02-10 02:26:56 +00:00
std : : unordered_map < String , std : : vector < UUID > > quota_to_user_ids ;
2019-11-04 19:17:27 +00:00
for ( const auto & user_name : user_names )
{
if ( config . has ( " users. " + user_name + " .quota " ) )
2021-11-18 20:54:18 +00:00
quota_to_user_ids [ config . getString ( " users. " + user_name + " .quota " ) ] . push_back ( generateID ( AccessEntityType : : USER , user_name ) ) ;
2019-11-04 19:17:27 +00:00
}
Poco : : Util : : AbstractConfiguration : : Keys quota_names ;
config . keys ( " quotas " , quota_names ) ;
2021-11-20 09:10:45 +00:00
2019-11-04 19:17:27 +00:00
std : : vector < AccessEntityPtr > quotas ;
quotas . reserve ( quota_names . size ( ) ) ;
2021-11-20 09:10:45 +00:00
2019-11-04 19:17:27 +00:00
for ( const auto & quota_name : quota_names )
{
2021-11-20 09:10:45 +00:00
try
{
auto it = quota_to_user_ids . find ( quota_name ) ;
const std : : vector < UUID > & quota_users = ( it ! = quota_to_user_ids . end ( ) ) ? std : : move ( it - > second ) : std : : vector < UUID > { } ;
quotas . push_back ( parseQuota ( config , quota_name , quota_users ) ) ;
}
catch ( Exception & e )
{
e . addMessage ( fmt : : format ( " while parsing quota '{}' in users configuration file " , quota_name ) ) ;
throw ;
}
2019-11-04 19:17:27 +00:00
}
2021-11-20 09:10:45 +00:00
2019-11-04 19:17:27 +00:00
return quotas ;
}
2019-11-17 11:57:02 +00:00
2022-05-06 23:37:23 +00:00
std : : vector < AccessEntityPtr > parseRowPolicies ( const Poco : : Util : : AbstractConfiguration & config , bool users_without_row_policies_can_read_rows )
2019-11-17 11:57:02 +00:00
{
2020-02-18 02:36:29 +00:00
std : : map < std : : pair < String /* database */ , String /* table */ > , std : : unordered_map < String /* user */ , String /* filter */ > > all_filters_map ;
2020-09-03 16:52:08 +00:00
2019-11-17 11:57:02 +00:00
Poco : : Util : : AbstractConfiguration : : Keys user_names ;
2020-09-03 16:52:08 +00:00
config . keys ( " users " , user_names ) ;
2019-11-17 11:57:02 +00:00
2020-09-03 16:52:08 +00:00
for ( const String & user_name : user_names )
2019-11-17 11:57:02 +00:00
{
2020-09-03 16:52:08 +00:00
const String databases_config = " users. " + user_name + " .databases " ;
if ( config . has ( databases_config ) )
2019-11-17 11:57:02 +00:00
{
2020-09-03 16:52:08 +00:00
Poco : : Util : : AbstractConfiguration : : Keys database_keys ;
config . keys ( databases_config , database_keys ) ;
/// Read tables within databases
for ( const String & database_key : database_keys )
2019-11-17 11:57:02 +00:00
{
2020-09-03 16:52:08 +00:00
const String database_config = databases_config + " . " + database_key ;
2019-11-17 11:57:02 +00:00
2020-09-03 16:52:08 +00:00
String database_name ;
if ( ( ( database_key = = " database " ) | | ( database_key . starts_with ( " database[ " ) ) ) & & config . has ( database_config + " [@name] " ) )
database_name = config . getString ( database_config + " [@name] " ) ;
else if ( size_t bracket_pos = database_key . find ( ' [ ' ) ; bracket_pos ! = std : : string : : npos )
database_name = database_key . substr ( 0 , bracket_pos ) ;
else
database_name = database_key ;
2020-07-31 22:58:01 +00:00
2020-09-03 16:52:08 +00:00
Poco : : Util : : AbstractConfiguration : : Keys table_keys ;
config . keys ( database_config , table_keys ) ;
/// Read table properties
for ( const String & table_key : table_keys )
{
String table_config = database_config + " . " + table_key ;
String table_name ;
if ( ( ( table_key = = " table " ) | | ( table_key . starts_with ( " table[ " ) ) ) & & config . has ( table_config + " [@name] " ) )
table_name = config . getString ( table_config + " [@name] " ) ;
else if ( size_t bracket_pos = table_key . find ( ' [ ' ) ; bracket_pos ! = std : : string : : npos )
table_name = table_key . substr ( 0 , bracket_pos ) ;
2020-07-31 22:58:01 +00:00
else
2020-09-03 16:52:08 +00:00
table_name = table_key ;
String filter_config = table_config + " .filter " ;
all_filters_map [ { database_name , table_name } ] [ user_name ] = config . getString ( filter_config ) ;
2019-11-17 11:57:02 +00:00
}
}
}
}
2020-02-18 02:36:29 +00:00
std : : vector < AccessEntityPtr > policies ;
for ( auto & [ database_and_table_name , user_to_filters ] : all_filters_map )
{
const auto & [ database , table_name ] = database_and_table_name ;
for ( const String & user_name : user_names )
{
2022-05-06 23:37:23 +00:00
String filter ;
2020-02-18 02:36:29 +00:00
auto it = user_to_filters . find ( user_name ) ;
2022-05-06 23:37:23 +00:00
if ( it ! = user_to_filters . end ( ) )
{
filter = it - > second ;
}
else
{
if ( users_without_row_policies_can_read_rows )
continue ;
else
filter = " 1 " ;
}
2020-02-18 02:36:29 +00:00
auto policy = std : : make_shared < RowPolicy > ( ) ;
2021-11-18 07:45:52 +00:00
policy - > setFullName ( user_name , database , table_name ) ;
2021-11-18 13:04:42 +00:00
policy - > filters [ static_cast < size_t > ( RowPolicyFilterType : : SELECT_FILTER ) ] = filter ;
2021-11-18 20:54:18 +00:00
policy - > to_roles . add ( generateID ( AccessEntityType : : USER , user_name ) ) ;
2020-02-18 02:36:29 +00:00
policies . push_back ( policy ) ;
}
}
2019-11-17 11:57:02 +00:00
return policies ;
}
2020-03-04 22:27:03 +00:00
SettingsProfileElements parseSettingsConstraints ( const Poco : : Util : : AbstractConfiguration & config ,
2020-08-03 21:16:28 +00:00
const String & path_to_constraints ,
2022-05-06 23:37:23 +00:00
const AccessControl & access_control )
2020-03-04 22:27:03 +00:00
{
SettingsProfileElements profile_elements ;
2020-07-19 14:59:07 +00:00
Poco : : Util : : AbstractConfiguration : : Keys keys ;
config . keys ( path_to_constraints , keys ) ;
2020-08-03 21:16:28 +00:00
2020-07-19 14:59:07 +00:00
for ( const String & setting_name : keys )
2020-03-04 22:27:03 +00:00
{
2022-05-06 23:37:23 +00:00
access_control . checkSettingNameIsAllowed ( setting_name ) ;
2020-08-03 21:16:28 +00:00
2020-03-04 22:27:03 +00:00
SettingsProfileElement profile_element ;
2020-07-19 14:59:07 +00:00
profile_element . setting_name = setting_name ;
2020-03-04 22:27:03 +00:00
Poco : : Util : : AbstractConfiguration : : Keys constraint_types ;
2020-07-19 14:59:07 +00:00
String path_to_name = path_to_constraints + " . " + setting_name ;
2020-03-04 22:27:03 +00:00
config . keys ( path_to_name , constraint_types ) ;
2020-08-03 21:16:28 +00:00
2020-03-04 22:27:03 +00:00
for ( const String & constraint_type : constraint_types )
{
if ( constraint_type = = " min " )
2020-07-20 09:57:17 +00:00
profile_element . min_value = Settings : : stringToValueUtil ( setting_name , config . getString ( path_to_name + " . " + constraint_type ) ) ;
2020-03-04 22:27:03 +00:00
else if ( constraint_type = = " max " )
2020-07-20 09:57:17 +00:00
profile_element . max_value = Settings : : stringToValueUtil ( setting_name , config . getString ( path_to_name + " . " + constraint_type ) ) ;
2020-03-04 22:27:03 +00:00
else if ( constraint_type = = " readonly " )
profile_element . readonly = true ;
else
2020-07-19 14:59:07 +00:00
throw Exception ( " Setting " + constraint_type + " value for " + setting_name + " isn't supported " , ErrorCodes : : NOT_IMPLEMENTED ) ;
2020-03-04 22:27:03 +00:00
}
profile_elements . push_back ( std : : move ( profile_element ) ) ;
}
2020-08-03 21:16:28 +00:00
2020-03-04 22:27:03 +00:00
return profile_elements ;
}
std : : shared_ptr < SettingsProfile > parseSettingsProfile (
const Poco : : Util : : AbstractConfiguration & config ,
2020-08-03 21:16:28 +00:00
const String & profile_name ,
2022-05-06 23:37:23 +00:00
const AccessControl & access_control )
2020-03-04 22:27:03 +00:00
{
auto profile = std : : make_shared < SettingsProfile > ( ) ;
profile - > setName ( profile_name ) ;
String profile_config = " profiles. " + profile_name ;
Poco : : Util : : AbstractConfiguration : : Keys keys ;
config . keys ( profile_config , keys ) ;
for ( const std : : string & key : keys )
{
if ( key = = " profile " | | key . starts_with ( " profile[ " ) )
{
String parent_profile_name = config . getString ( profile_config + " . " + key ) ;
SettingsProfileElement profile_element ;
2021-11-18 20:54:18 +00:00
profile_element . parent_profile = generateID ( AccessEntityType : : SETTINGS_PROFILE , parent_profile_name ) ;
2020-03-04 22:27:03 +00:00
profile - > elements . emplace_back ( std : : move ( profile_element ) ) ;
continue ;
}
if ( key = = " constraints " | | key . starts_with ( " constraints[ " ) )
{
2022-05-06 23:37:23 +00:00
profile - > elements . merge ( parseSettingsConstraints ( config , profile_config + " . " + key , access_control ) ) ;
2020-03-04 22:27:03 +00:00
continue ;
}
2020-07-19 14:59:07 +00:00
const auto & setting_name = key ;
2022-05-06 23:37:23 +00:00
access_control . checkSettingNameIsAllowed ( setting_name ) ;
2020-08-03 21:16:28 +00:00
2020-03-04 22:27:03 +00:00
SettingsProfileElement profile_element ;
2020-07-19 14:59:07 +00:00
profile_element . setting_name = setting_name ;
2020-07-20 09:57:17 +00:00
profile_element . value = Settings : : stringToValueUtil ( setting_name , config . getString ( profile_config + " . " + key ) ) ;
2020-03-04 22:27:03 +00:00
profile - > elements . emplace_back ( std : : move ( profile_element ) ) ;
}
return profile ;
}
2020-08-03 21:16:28 +00:00
std : : vector < AccessEntityPtr > parseSettingsProfiles (
const Poco : : Util : : AbstractConfiguration & config ,
2022-05-06 23:37:23 +00:00
const AccessControl & access_control )
2020-03-04 22:27:03 +00:00
{
Poco : : Util : : AbstractConfiguration : : Keys profile_names ;
config . keys ( " profiles " , profile_names ) ;
2021-11-20 09:10:45 +00:00
std : : vector < AccessEntityPtr > profiles ;
profiles . reserve ( profile_names . size ( ) ) ;
2020-03-04 22:27:03 +00:00
for ( const auto & profile_name : profile_names )
2021-11-20 09:10:45 +00:00
{
try
{
2022-05-06 23:37:23 +00:00
profiles . push_back ( parseSettingsProfile ( config , profile_name , access_control ) ) ;
2021-11-20 09:10:45 +00:00
}
catch ( Exception & e )
{
e . addMessage ( fmt : : format ( " while parsing profile '{}' in users configuration file " , profile_name ) ) ;
throw ;
}
}
2020-09-03 16:52:08 +00:00
2020-03-04 22:27:03 +00:00
return profiles ;
}
2019-11-09 15:33:07 +00:00
}
2022-05-16 18:43:55 +00:00
UsersConfigAccessStorage : : UsersConfigAccessStorage ( const String & storage_name_ , AccessControl & access_control_ )
2022-06-15 18:25:13 +00:00
: IAccessStorage ( storage_name_ )
, access_control ( access_control_ )
, memory_storage ( storage_name_ , /* allow_backup= */ false , access_control . getChangesNotifier ( ) )
2020-08-03 21:16:28 +00:00
{
}
2020-08-05 19:54:06 +00:00
UsersConfigAccessStorage : : ~ UsersConfigAccessStorage ( ) = default ;
2020-08-03 21:16:28 +00:00
2020-09-14 22:51:53 +00:00
String UsersConfigAccessStorage : : getStorageParamsJSON ( ) const
{
std : : lock_guard lock { load_mutex } ;
Poco : : JSON : : Object json ;
if ( ! path . empty ( ) )
json . set ( " path " , path ) ;
2020-11-09 19:07:38 +00:00
std : : ostringstream oss ; // STYLE_CHECK_ALLOW_STD_STRING_STREAM
2020-11-07 00:14:53 +00:00
oss . exceptions ( std : : ios : : failbit ) ;
2020-09-14 22:51:53 +00:00
Poco : : JSON : : Stringifier : : stringify ( json , oss ) ;
return oss . str ( ) ;
}
String UsersConfigAccessStorage : : getPath ( ) const
2020-08-12 14:22:37 +00:00
{
std : : lock_guard lock { load_mutex } ;
return path ;
}
2020-09-14 22:51:53 +00:00
bool UsersConfigAccessStorage : : isPathEqual ( const String & path_ ) const
{
return getPath ( ) = = path_ ;
}
2022-02-28 18:51:49 +00:00
void UsersConfigAccessStorage : : setConfig ( const Poco : : Util : : AbstractConfiguration & config )
2020-08-05 19:54:06 +00:00
{
std : : lock_guard lock { load_mutex } ;
path . clear ( ) ;
config_reloader . reset ( ) ;
2022-02-28 18:51:49 +00:00
parseFromConfig ( config ) ;
2020-08-05 19:54:06 +00:00
}
2022-02-28 18:51:49 +00:00
void UsersConfigAccessStorage : : parseFromConfig ( const Poco : : Util : : AbstractConfiguration & config )
2019-11-09 15:33:07 +00:00
{
2021-11-20 09:10:45 +00:00
try
{
2022-05-06 23:37:23 +00:00
bool no_password_allowed = access_control . isNoPasswordAllowed ( ) ;
bool plaintext_password_allowed = access_control . isPlaintextPasswordAllowed ( ) ;
2021-11-20 09:10:45 +00:00
std : : vector < std : : pair < UUID , AccessEntityPtr > > all_entities ;
2022-03-14 14:32:58 +00:00
for ( const auto & entity : parseUsers ( config , no_password_allowed , plaintext_password_allowed ) )
2021-11-20 09:10:45 +00:00
all_entities . emplace_back ( generateID ( * entity ) , entity ) ;
for ( const auto & entity : parseQuotas ( config ) )
all_entities . emplace_back ( generateID ( * entity ) , entity ) ;
2022-05-06 23:37:23 +00:00
for ( const auto & entity : parseRowPolicies ( config , access_control . isEnabledUsersWithoutRowPoliciesCanReadRows ( ) ) )
2021-11-20 09:10:45 +00:00
all_entities . emplace_back ( generateID ( * entity ) , entity ) ;
2022-05-06 23:37:23 +00:00
for ( const auto & entity : parseSettingsProfiles ( config , access_control ) )
2021-11-20 09:10:45 +00:00
all_entities . emplace_back ( generateID ( * entity ) , entity ) ;
memory_storage . setAll ( all_entities ) ;
}
catch ( Exception & e )
{
e . addMessage ( fmt : : format ( " while loading {} " , path . empty ( ) ? " configuration " : ( " configuration file " + quoteString ( path ) ) ) ) ;
throw ;
}
2019-11-09 15:33:07 +00:00
}
2020-08-18 15:08:50 +00:00
void UsersConfigAccessStorage : : load (
const String & users_config_path ,
const String & include_from_path ,
const String & preprocessed_dir ,
2022-02-28 18:51:49 +00:00
const zkutil : : GetZooKeeper & get_zookeeper_function )
2020-08-05 19:54:06 +00:00
{
2021-11-20 09:10:45 +00:00
std : : lock_guard lock { load_mutex } ;
path = std : : filesystem : : path { users_config_path } . lexically_normal ( ) ;
config_reloader . reset ( ) ;
config_reloader = std : : make_unique < ConfigReloader > (
users_config_path ,
include_from_path ,
preprocessed_dir ,
zkutil : : ZooKeeperNodeCache ( get_zookeeper_function ) ,
std : : make_shared < Poco : : Event > ( ) ,
[ & ] ( Poco : : AutoPtr < Poco : : Util : : AbstractConfiguration > new_config , bool /*initial_loading*/ )
{
Settings : : checkNoSettingNamesAtTopLevel ( * new_config , users_config_path ) ;
2022-05-16 18:43:55 +00:00
parseFromConfig ( * new_config ) ;
access_control . getChangesNotifier ( ) . sendNotifications ( ) ;
2021-11-20 09:10:45 +00:00
} ,
/* already_loaded = */ false ) ;
2020-08-05 19:54:06 +00:00
}
void UsersConfigAccessStorage : : reload ( )
{
std : : lock_guard lock { load_mutex } ;
if ( config_reloader )
config_reloader - > reload ( ) ;
}
void UsersConfigAccessStorage : : startPeriodicReloading ( )
{
std : : lock_guard lock { load_mutex } ;
if ( config_reloader )
config_reloader - > start ( ) ;
}
2019-11-09 15:33:07 +00:00
2021-11-21 19:39:36 +00:00
void UsersConfigAccessStorage : : stopPeriodicReloading ( )
{
std : : lock_guard lock { load_mutex } ;
if ( config_reloader )
config_reloader - > stop ( ) ;
}
2021-11-18 20:54:18 +00:00
std : : optional < UUID > UsersConfigAccessStorage : : findImpl ( AccessEntityType type , const String & name ) const
2019-11-09 15:33:07 +00:00
{
return memory_storage . find ( type , name ) ;
}
2021-11-18 20:54:18 +00:00
std : : vector < UUID > UsersConfigAccessStorage : : findAllImpl ( AccessEntityType type ) const
2019-11-09 15:33:07 +00:00
{
return memory_storage . findAll ( type ) ;
}
2021-11-22 21:50:15 +00:00
bool UsersConfigAccessStorage : : exists ( const UUID & id ) const
2019-11-09 15:33:07 +00:00
{
return memory_storage . exists ( id ) ;
}
2021-12-11 16:29:38 +00:00
AccessEntityPtr UsersConfigAccessStorage : : readImpl ( const UUID & id , bool throw_if_not_exists ) const
2019-11-09 15:33:07 +00:00
{
2021-12-11 16:29:38 +00:00
return memory_storage . read ( id , throw_if_not_exists ) ;
2019-11-09 15:33:07 +00:00
}
2022-06-15 18:25:13 +00:00
std : : optional < std : : pair < String , AccessEntityType > > UsersConfigAccessStorage : : readNameWithTypeImpl ( const UUID & id , bool throw_if_not_exists ) const
2019-11-09 15:33:07 +00:00
{
2022-06-15 18:25:13 +00:00
return memory_storage . readNameWithType ( id , throw_if_not_exists ) ;
2019-11-09 15:33:07 +00:00
}
}