762
940
"""Set a per-branch configuration option"""
763
941
self.branch.lock_write()
765
cfg_obj = self._get_config()
770
obj = cfg_obj[section]
772
cfg_obj[section] = {}
773
obj = cfg_obj[section]
775
out_file = StringIO()
776
cfg_obj.write(out_file)
778
self.branch.control_files.put('branch.conf', out_file)
943
self._config.set_option(value, name, section)
780
945
self.branch.unlock()
948
class AuthenticationConfig(object):
949
"""The authentication configuration file based on a ini file.
951
Implements the authentication.conf file described in
952
doc/developers/authentication-ring.txt.
955
def __init__(self, _file=None):
956
self._config = None # The ConfigObj
958
self._filename = authentication_config_filename()
959
self._input = self._filename = authentication_config_filename()
961
# Tests can provide a string as _file
962
self._filename = None
965
def _get_config(self):
966
if self._config is not None:
969
# FIXME: Should we validate something here ? Includes: empty
970
# sections are useless, at least one of
971
# user/password/password_encoding should be defined, etc.
973
# Note: the encoding below declares that the file itself is utf-8
974
# encoded, but the values in the ConfigObj are always Unicode.
975
self._config = ConfigObj(self._input, encoding='utf-8')
976
except configobj.ConfigObjError, e:
977
raise errors.ParseConfigError(e.errors, e.config.filename)
981
"""Save the config file, only tests should use it for now."""
982
conf_dir = os.path.dirname(self._filename)
983
ensure_config_dir_exists(conf_dir)
984
self._get_config().write(file(self._filename, 'wb'))
986
def _set_option(self, section_name, option_name, value):
987
"""Set an authentication configuration option"""
988
conf = self._get_config()
989
section = conf.get(section_name)
992
section = conf[section]
993
section[option_name] = value
996
def get_credentials(self, scheme, host, port=None, user=None, path=None):
997
"""Returns the matching credentials from authentication.conf file.
999
:param scheme: protocol
1001
:param host: the server address
1003
:param port: the associated port (optional)
1005
:param user: login (optional)
1007
:param path: the absolute path on the server (optional)
1009
:return: A dict containing the matching credentials or None.
1011
- name: the section name of the credentials in the
1012
authentication.conf file,
1013
- user: can't de different from the provided user if any,
1014
- password: the decoded password, could be None if the credential
1015
defines only the user
1016
- verify_certificates: https specific, True if the server
1017
certificate should be verified, False otherwise.
1020
for auth_def_name, auth_def in self._get_config().items():
1021
if type(auth_def) is not configobj.Section:
1022
raise ValueError("%s defined outside a section" % auth_def_name)
1024
a_scheme, a_host, a_user, a_path = map(
1025
auth_def.get, ['scheme', 'host', 'user', 'path'])
1028
a_port = auth_def.as_int('port')
1032
raise ValueError("'port' not numeric in %s" % auth_def_name)
1034
a_verify_certificates = auth_def.as_bool('verify_certificates')
1036
a_verify_certificates = True
1039
"'verify_certificates' not boolean in %s" % auth_def_name)
1042
if a_scheme is not None and scheme != a_scheme:
1044
if a_host is not None:
1045
if not (host == a_host
1046
or (a_host.startswith('.') and host.endswith(a_host))):
1048
if a_port is not None and port != a_port:
1050
if (a_path is not None and path is not None
1051
and not path.startswith(a_path)):
1053
if (a_user is not None and user is not None
1054
and a_user != user):
1055
# Never contradict the caller about the user to be used
1060
credentials = dict(name=auth_def_name,
1062
password=auth_def.get('password', None),
1063
verify_certificates=a_verify_certificates)
1064
self.decode_password(credentials,
1065
auth_def.get('password_encoding', None))
1066
if 'auth' in debug.debug_flags:
1067
trace.mutter("Using authentication section: %r", auth_def_name)
1072
def set_credentials(self, name, host, user, scheme=None, password=None,
1073
port=None, path=None, verify_certificates=None):
1074
"""Set authentication credentials for a host.
1076
Any existing credentials with matching scheme, host, port and path
1077
will be deleted, regardless of name.
1079
:param name: An arbitrary name to describe this set of credentials.
1080
:param host: Name of the host that accepts these credentials.
1081
:param user: The username portion of these credentials.
1082
:param scheme: The URL scheme (e.g. ssh, http) the credentials apply
1084
:param password: Password portion of these credentials.
1085
:param port: The IP port on the host that these credentials apply to.
1086
:param path: A filesystem path on the host that these credentials
1088
:param verify_certificates: On https, verify server certificates if
1091
values = {'host': host, 'user': user}
1092
if password is not None:
1093
values['password'] = password
1094
if scheme is not None:
1095
values['scheme'] = scheme
1096
if port is not None:
1097
values['port'] = '%d' % port
1098
if path is not None:
1099
values['path'] = path
1100
if verify_certificates is not None:
1101
values['verify_certificates'] = str(verify_certificates)
1102
config = self._get_config()
1104
for section, existing_values in config.items():
1105
for key in ('scheme', 'host', 'port', 'path'):
1106
if existing_values.get(key) != values.get(key):
1110
config.update({name: values})
1113
def get_user(self, scheme, host, port=None,
1114
realm=None, path=None, prompt=None):
1115
"""Get a user from authentication file.
1117
:param scheme: protocol
1119
:param host: the server address
1121
:param port: the associated port (optional)
1123
:param realm: the realm sent by the server (optional)
1125
:param path: the absolute path on the server (optional)
1127
:return: The found user.
1129
credentials = self.get_credentials(scheme, host, port, user=None,
1131
if credentials is not None:
1132
user = credentials['user']
1137
def get_password(self, scheme, host, user, port=None,
1138
realm=None, path=None, prompt=None):
1139
"""Get a password from authentication file or prompt the user for one.
1141
:param scheme: protocol
1143
:param host: the server address
1145
:param port: the associated port (optional)
1149
:param realm: the realm sent by the server (optional)
1151
:param path: the absolute path on the server (optional)
1153
:return: The found password or the one entered by the user.
1155
credentials = self.get_credentials(scheme, host, port, user, path)
1156
if credentials is not None:
1157
password = credentials['password']
1158
if password is not None and scheme is 'ssh':
1159
trace.warning('password ignored in section [%s],'
1160
' use an ssh agent instead'
1161
% credentials['name'])
1165
# Prompt user only if we could't find a password
1166
if password is None:
1168
# Create a default prompt suitable for most cases
1169
prompt = '%s' % scheme.upper() + ' %(user)s@%(host)s password'
1170
# Special handling for optional fields in the prompt
1171
if port is not None:
1172
prompt_host = '%s:%d' % (host, port)
1175
password = ui.ui_factory.get_password(prompt,
1176
host=prompt_host, user=user)
1179
def decode_password(self, credentials, encoding):
1181
cs = credential_store_registry.get_credential_store(encoding)
1183
raise ValueError('%r is not a known password_encoding' % encoding)
1184
credentials['password'] = cs.decode_password(credentials)
1188
class CredentialStoreRegistry(registry.Registry):
1189
"""A class that registers credential stores.
1191
A credential store provides access to credentials via the password_encoding
1192
field in authentication.conf sections.
1194
Except for stores provided by bzr itself,most stores are expected to be
1195
provided by plugins that will therefore use
1196
register_lazy(password_encoding, module_name, member_name, help=help,
1197
info=info) to install themselves.
1200
def get_credential_store(self, encoding=None):
1201
cs = self.get(encoding)
1207
credential_store_registry = CredentialStoreRegistry()
1210
class CredentialStore(object):
1211
"""An abstract class to implement storage for credentials"""
1213
def decode_password(self, credentials):
1214
"""Returns a password for the provided credentials in clear text."""
1215
raise NotImplementedError(self.decode_password)
1218
class PlainTextCredentialStore(CredentialStore):
1219
"""Plain text credential store for the authentication.conf file."""
1221
def decode_password(self, credentials):
1222
"""See CredentialStore.decode_password."""
1223
return credentials['password']
1226
credential_store_registry.register('plain', PlainTextCredentialStore,
1227
help=PlainTextCredentialStore.__doc__)
1228
credential_store_registry.default_key = 'plain'
1231
class BzrDirConfig(object):
1233
def __init__(self, transport):
1234
self._config = TransportConfig(transport, 'control.conf')
1236
def set_default_stack_on(self, value):
1237
"""Set the default stacking location.
1239
It may be set to a location, or None.
1241
This policy affects all branches contained by this bzrdir, except for
1242
those under repositories.
1245
self._config.set_option('', 'default_stack_on')
1247
self._config.set_option(value, 'default_stack_on')
1249
def get_default_stack_on(self):
1250
"""Return the default stacking location.
1252
This will either be a location, or None.
1254
This policy affects all branches contained by this bzrdir, except for
1255
those under repositories.
1257
value = self._config.get_option('default_stack_on')
1263
class TransportConfig(object):
1264
"""A Config that reads/writes a config file on a Transport.
1266
It is a low-level object that considers config data to be name/value pairs
1267
that may be associated with a section. Assigning meaning to the these
1268
values is done at higher levels like TreeConfig.
1271
def __init__(self, transport, filename):
1272
self._transport = transport
1273
self._filename = filename
1275
def get_option(self, name, section=None, default=None):
1276
"""Return the value associated with a named option.
1278
:param name: The name of the value
1279
:param section: The section the option is in (if any)
1280
:param default: The value to return if the value is not set
1281
:return: The value or default value
1283
configobj = self._get_configobj()
1285
section_obj = configobj
1288
section_obj = configobj[section]
1291
return section_obj.get(name, default)
1293
def set_option(self, value, name, section=None):
1294
"""Set the value associated with a named option.
1296
:param value: The value to set
1297
:param name: The name of the value to set
1298
:param section: The section the option is in (if any)
1300
configobj = self._get_configobj()
1302
configobj[name] = value
1304
configobj.setdefault(section, {})[name] = value
1305
self._set_configobj(configobj)
1307
def _get_configobj(self):
1309
return ConfigObj(self._transport.get(self._filename),
1311
except errors.NoSuchFile:
1312
return ConfigObj(encoding='utf-8')
1314
def _set_configobj(self, configobj):
1315
out_file = StringIO()
1316
configobj.write(out_file)
1318
self._transport.put_file(self._filename, out_file)