963
803
except ImportError:
966
user_encoding = osutils.get_user_encoding()
967
realname = username = getpass.getuser().decode(user_encoding)
806
realname = username = getpass.getuser().decode(bzrlib.user_encoding)
968
807
except UnicodeDecodeError:
969
808
raise errors.BzrError("Can't decode username as %s." % \
809
bzrlib.user_encoding)
972
811
return realname, (username + '@' + socket.gethostname())
975
def parse_username(username):
976
"""Parse e-mail username and return a (name, address) tuple."""
977
match = re.match(r'(.*?)\s*<?([\w+.-]+@[\w+.-]+)>?', username)
979
return (username, '')
981
return (match.group(1), match.group(2))
984
814
def extract_email_address(e):
985
815
"""Return just the address part of an email string.
987
That is just the user@domain part, nothing else.
817
That is just the user@domain part, nothing else.
988
818
This part is required to contain only ascii characters.
989
819
If it can't be extracted, raises an error.
991
821
>>> extract_email_address('Jane Tester <jane@test.com>')
994
name, email = parse_username(e)
824
m = re.search(r'[\w+.-]+@[\w+.-]+', e)
996
826
raise errors.NoEmailInUsername(e)
1000
830
class TreeConfig(IniBasedConfig):
1001
831
"""Branch configuration data associated with its contents, not location"""
1003
# XXX: Really needs a better name, as this is not part of the tree! -- mbp 20080507
1005
832
def __init__(self, branch):
1006
self._config = branch._get_config()
1007
833
self.branch = branch
1009
835
def _get_parser(self, file=None):
1010
836
if file is not None:
1011
837
return IniBasedConfig._get_parser(file)
1012
return self._config._get_configobj()
838
return self._get_config()
840
def _get_config(self):
842
obj = ConfigObj(self.branch.control_files.get('branch.conf'),
844
except errors.NoSuchFile:
845
obj = ConfigObj(encoding='utf=8')
1014
848
def get_option(self, name, section=None, default=None):
1015
849
self.branch.lock_read()
1017
return self._config.get_option(name, section, default)
851
obj = self._get_config()
853
if section is not None:
1019
859
self.branch.unlock()
1021
862
def set_option(self, value, name, section=None):
1022
863
"""Set a per-branch configuration option"""
1023
864
self.branch.lock_write()
1025
self._config.set_option(value, name, section)
866
cfg_obj = self._get_config()
871
obj = cfg_obj[section]
873
cfg_obj[section] = {}
874
obj = cfg_obj[section]
876
out_file = StringIO()
877
cfg_obj.write(out_file)
879
self.branch.control_files.put('branch.conf', out_file)
1027
881
self.branch.unlock()
1030
class AuthenticationConfig(object):
1031
"""The authentication configuration file based on a ini file.
1033
Implements the authentication.conf file described in
1034
doc/developers/authentication-ring.txt.
1037
def __init__(self, _file=None):
1038
self._config = None # The ConfigObj
1040
self._filename = authentication_config_filename()
1041
self._input = self._filename = authentication_config_filename()
1043
# Tests can provide a string as _file
1044
self._filename = None
1047
def _get_config(self):
1048
if self._config is not None:
1051
# FIXME: Should we validate something here ? Includes: empty
1052
# sections are useless, at least one of
1053
# user/password/password_encoding should be defined, etc.
1055
# Note: the encoding below declares that the file itself is utf-8
1056
# encoded, but the values in the ConfigObj are always Unicode.
1057
self._config = ConfigObj(self._input, encoding='utf-8')
1058
except configobj.ConfigObjError, e:
1059
raise errors.ParseConfigError(e.errors, e.config.filename)
1063
"""Save the config file, only tests should use it for now."""
1064
conf_dir = os.path.dirname(self._filename)
1065
ensure_config_dir_exists(conf_dir)
1066
self._get_config().write(file(self._filename, 'wb'))
1068
def _set_option(self, section_name, option_name, value):
1069
"""Set an authentication configuration option"""
1070
conf = self._get_config()
1071
section = conf.get(section_name)
1074
section = conf[section]
1075
section[option_name] = value
1078
def get_credentials(self, scheme, host, port=None, user=None, path=None,
1080
"""Returns the matching credentials from authentication.conf file.
1082
:param scheme: protocol
1084
:param host: the server address
1086
:param port: the associated port (optional)
1088
:param user: login (optional)
1090
:param path: the absolute path on the server (optional)
1092
:param realm: the http authentication realm (optional)
1094
:return: A dict containing the matching credentials or None.
1096
- name: the section name of the credentials in the
1097
authentication.conf file,
1098
- user: can't be different from the provided user if any,
1099
- scheme: the server protocol,
1100
- host: the server address,
1101
- port: the server port (can be None),
1102
- path: the absolute server path (can be None),
1103
- realm: the http specific authentication realm (can be None),
1104
- password: the decoded password, could be None if the credential
1105
defines only the user
1106
- verify_certificates: https specific, True if the server
1107
certificate should be verified, False otherwise.
1110
for auth_def_name, auth_def in self._get_config().items():
1111
if type(auth_def) is not configobj.Section:
1112
raise ValueError("%s defined outside a section" % auth_def_name)
1114
a_scheme, a_host, a_user, a_path = map(
1115
auth_def.get, ['scheme', 'host', 'user', 'path'])
1118
a_port = auth_def.as_int('port')
1122
raise ValueError("'port' not numeric in %s" % auth_def_name)
1124
a_verify_certificates = auth_def.as_bool('verify_certificates')
1126
a_verify_certificates = True
1129
"'verify_certificates' not boolean in %s" % auth_def_name)
1132
if a_scheme is not None and scheme != a_scheme:
1134
if a_host is not None:
1135
if not (host == a_host
1136
or (a_host.startswith('.') and host.endswith(a_host))):
1138
if a_port is not None and port != a_port:
1140
if (a_path is not None and path is not None
1141
and not path.startswith(a_path)):
1143
if (a_user is not None and user is not None
1144
and a_user != user):
1145
# Never contradict the caller about the user to be used
1150
# Prepare a credentials dictionary with additional keys
1151
# for the credential providers
1152
credentials = dict(name=auth_def_name,
1159
password=auth_def.get('password', None),
1160
verify_certificates=a_verify_certificates)
1161
# Decode the password in the credentials (or get one)
1162
self.decode_password(credentials,
1163
auth_def.get('password_encoding', None))
1164
if 'auth' in debug.debug_flags:
1165
trace.mutter("Using authentication section: %r", auth_def_name)
1168
if credentials is None:
1169
# No credentials were found in authentication.conf, try the fallback
1170
# credentials stores.
1171
credentials = credential_store_registry.get_fallback_credentials(
1172
scheme, host, port, user, path, realm)
1176
def set_credentials(self, name, host, user, scheme=None, password=None,
1177
port=None, path=None, verify_certificates=None,
1179
"""Set authentication credentials for a host.
1181
Any existing credentials with matching scheme, host, port and path
1182
will be deleted, regardless of name.
1184
:param name: An arbitrary name to describe this set of credentials.
1185
:param host: Name of the host that accepts these credentials.
1186
:param user: The username portion of these credentials.
1187
:param scheme: The URL scheme (e.g. ssh, http) the credentials apply
1189
:param password: Password portion of these credentials.
1190
:param port: The IP port on the host that these credentials apply to.
1191
:param path: A filesystem path on the host that these credentials
1193
:param verify_certificates: On https, verify server certificates if
1195
:param realm: The http authentication realm (optional).
1197
values = {'host': host, 'user': user}
1198
if password is not None:
1199
values['password'] = password
1200
if scheme is not None:
1201
values['scheme'] = scheme
1202
if port is not None:
1203
values['port'] = '%d' % port
1204
if path is not None:
1205
values['path'] = path
1206
if verify_certificates is not None:
1207
values['verify_certificates'] = str(verify_certificates)
1208
if realm is not None:
1209
values['realm'] = realm
1210
config = self._get_config()
1212
for section, existing_values in config.items():
1213
for key in ('scheme', 'host', 'port', 'path', 'realm'):
1214
if existing_values.get(key) != values.get(key):
1218
config.update({name: values})
1221
def get_user(self, scheme, host, port=None, realm=None, path=None,
1222
prompt=None, ask=False, default=None):
1223
"""Get a user from authentication file.
1225
:param scheme: protocol
1227
:param host: the server address
1229
:param port: the associated port (optional)
1231
:param realm: the realm sent by the server (optional)
1233
:param path: the absolute path on the server (optional)
1235
:param ask: Ask the user if there is no explicitly configured username
1238
:param default: The username returned if none is defined (optional).
1240
:return: The found user.
1242
credentials = self.get_credentials(scheme, host, port, user=None,
1243
path=path, realm=realm)
1244
if credentials is not None:
1245
user = credentials['user']
1251
# Create a default prompt suitable for most cases
1252
prompt = scheme.upper() + ' %(host)s username'
1253
# Special handling for optional fields in the prompt
1254
if port is not None:
1255
prompt_host = '%s:%d' % (host, port)
1258
user = ui.ui_factory.get_username(prompt, host=prompt_host)
1263
def get_password(self, scheme, host, user, port=None,
1264
realm=None, path=None, prompt=None):
1265
"""Get a password from authentication file or prompt the user for one.
1267
:param scheme: protocol
1269
:param host: the server address
1271
:param port: the associated port (optional)
1275
:param realm: the realm sent by the server (optional)
1277
:param path: the absolute path on the server (optional)
1279
:return: The found password or the one entered by the user.
1281
credentials = self.get_credentials(scheme, host, port, user, path,
1283
if credentials is not None:
1284
password = credentials['password']
1285
if password is not None and scheme is 'ssh':
1286
trace.warning('password ignored in section [%s],'
1287
' use an ssh agent instead'
1288
% credentials['name'])
1292
# Prompt user only if we could't find a password
1293
if password is None:
1295
# Create a default prompt suitable for most cases
1296
prompt = '%s' % scheme.upper() + ' %(user)s@%(host)s password'
1297
# Special handling for optional fields in the prompt
1298
if port is not None:
1299
prompt_host = '%s:%d' % (host, port)
1302
password = ui.ui_factory.get_password(prompt,
1303
host=prompt_host, user=user)
1306
def decode_password(self, credentials, encoding):
1308
cs = credential_store_registry.get_credential_store(encoding)
1310
raise ValueError('%r is not a known password_encoding' % encoding)
1311
credentials['password'] = cs.decode_password(credentials)
1315
class CredentialStoreRegistry(registry.Registry):
1316
"""A class that registers credential stores.
1318
A credential store provides access to credentials via the password_encoding
1319
field in authentication.conf sections.
1321
Except for stores provided by bzr itself, most stores are expected to be
1322
provided by plugins that will therefore use
1323
register_lazy(password_encoding, module_name, member_name, help=help,
1324
fallback=fallback) to install themselves.
1326
A fallback credential store is one that is queried if no credentials can be
1327
found via authentication.conf.
1330
def get_credential_store(self, encoding=None):
1331
cs = self.get(encoding)
1336
def is_fallback(self, name):
1337
"""Check if the named credentials store should be used as fallback."""
1338
return self.get_info(name)
1340
def get_fallback_credentials(self, scheme, host, port=None, user=None,
1341
path=None, realm=None):
1342
"""Request credentials from all fallback credentials stores.
1344
The first credentials store that can provide credentials wins.
1347
for name in self.keys():
1348
if not self.is_fallback(name):
1350
cs = self.get_credential_store(name)
1351
credentials = cs.get_credentials(scheme, host, port, user,
1353
if credentials is not None:
1354
# We found some credentials
1358
def register(self, key, obj, help=None, override_existing=False,
1360
"""Register a new object to a name.
1362
:param key: This is the key to use to request the object later.
1363
:param obj: The object to register.
1364
:param help: Help text for this entry. This may be a string or
1365
a callable. If it is a callable, it should take two
1366
parameters (registry, key): this registry and the key that
1367
the help was registered under.
1368
:param override_existing: Raise KeyErorr if False and something has
1369
already been registered for that key. If True, ignore if there
1370
is an existing key (always register the new value).
1371
:param fallback: Whether this credential store should be
1374
return super(CredentialStoreRegistry,
1375
self).register(key, obj, help, info=fallback,
1376
override_existing=override_existing)
1378
def register_lazy(self, key, module_name, member_name,
1379
help=None, override_existing=False,
1381
"""Register a new credential store to be loaded on request.
1383
:param module_name: The python path to the module. Such as 'os.path'.
1384
:param member_name: The member of the module to return. If empty or
1385
None, get() will return the module itself.
1386
:param help: Help text for this entry. This may be a string or
1388
:param override_existing: If True, replace the existing object
1389
with the new one. If False, if there is already something
1390
registered with the same key, raise a KeyError
1391
:param fallback: Whether this credential store should be
1394
return super(CredentialStoreRegistry, self).register_lazy(
1395
key, module_name, member_name, help,
1396
info=fallback, override_existing=override_existing)
1399
credential_store_registry = CredentialStoreRegistry()
1402
class CredentialStore(object):
1403
"""An abstract class to implement storage for credentials"""
1405
def decode_password(self, credentials):
1406
"""Returns a clear text password for the provided credentials."""
1407
raise NotImplementedError(self.decode_password)
1409
def get_credentials(self, scheme, host, port=None, user=None, path=None,
1411
"""Return the matching credentials from this credential store.
1413
This method is only called on fallback credential stores.
1415
raise NotImplementedError(self.get_credentials)
1419
class PlainTextCredentialStore(CredentialStore):
1420
__doc__ = """Plain text credential store for the authentication.conf file"""
1422
def decode_password(self, credentials):
1423
"""See CredentialStore.decode_password."""
1424
return credentials['password']
1427
credential_store_registry.register('plain', PlainTextCredentialStore,
1428
help=PlainTextCredentialStore.__doc__)
1429
credential_store_registry.default_key = 'plain'
1432
class BzrDirConfig(object):
1434
def __init__(self, bzrdir):
1435
self._bzrdir = bzrdir
1436
self._config = bzrdir._get_config()
1438
def set_default_stack_on(self, value):
1439
"""Set the default stacking location.
1441
It may be set to a location, or None.
1443
This policy affects all branches contained by this bzrdir, except for
1444
those under repositories.
1446
if self._config is None:
1447
raise errors.BzrError("Cannot set configuration in %s" % self._bzrdir)
1449
self._config.set_option('', 'default_stack_on')
1451
self._config.set_option(value, 'default_stack_on')
1453
def get_default_stack_on(self):
1454
"""Return the default stacking location.
1456
This will either be a location, or None.
1458
This policy affects all branches contained by this bzrdir, except for
1459
those under repositories.
1461
if self._config is None:
1463
value = self._config.get_option('default_stack_on')
1469
class TransportConfig(object):
1470
"""A Config that reads/writes a config file on a Transport.
1472
It is a low-level object that considers config data to be name/value pairs
1473
that may be associated with a section. Assigning meaning to the these
1474
values is done at higher levels like TreeConfig.
1477
def __init__(self, transport, filename):
1478
self._transport = transport
1479
self._filename = filename
1481
def get_option(self, name, section=None, default=None):
1482
"""Return the value associated with a named option.
1484
:param name: The name of the value
1485
:param section: The section the option is in (if any)
1486
:param default: The value to return if the value is not set
1487
:return: The value or default value
1489
configobj = self._get_configobj()
1491
section_obj = configobj
1494
section_obj = configobj[section]
1497
return section_obj.get(name, default)
1499
def set_option(self, value, name, section=None):
1500
"""Set the value associated with a named option.
1502
:param value: The value to set
1503
:param name: The name of the value to set
1504
:param section: The section the option is in (if any)
1506
configobj = self._get_configobj()
1508
configobj[name] = value
1510
configobj.setdefault(section, {})[name] = value
1511
self._set_configobj(configobj)
1513
def _get_config_file(self):
1515
return StringIO(self._transport.get_bytes(self._filename))
1516
except errors.NoSuchFile:
1519
def _get_configobj(self):
1520
return ConfigObj(self._get_config_file(), encoding='utf-8')
1522
def _set_configobj(self, configobj):
1523
out_file = StringIO()
1524
configobj.write(out_file)
1526
self._transport.put_file(self._filename, out_file)