15
15
# You should have received a copy of the GNU General Public License
16
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
19
"""Configuration that affects the behaviour of Bazaar.
45
45
editor - this option sets the pop up editor to use during commits.
46
46
email - this option sets the user id bzr will use when committing.
47
47
check_signatures - this option controls whether bzr will require good gpg
48
signatures, ignore them, or check them if they are
48
signatures, ignore them, or check them if they are
50
create_signatures - this option controls whether bzr will always create
50
create_signatures - this option controls whether bzr will always create
51
51
gpg signatures, never create them, or create them if the
52
52
branch is configured to require them.
53
53
log_format - this option sets the default log format. Possible values are
146
146
class Config(object):
147
147
"""A configuration policy - what username, editor, gpg needs etc."""
150
super(Config, self).__init__()
149
152
def get_editor(self):
150
153
"""Get the users pop up editor."""
151
154
raise NotImplementedError
156
def get_change_editor(self, old_tree, new_tree):
157
from bzrlib import diff
158
cmd = self._get_change_editor()
161
return diff.DiffFromTool.from_string(cmd, old_tree, new_tree,
153
165
def get_mail_client(self):
154
166
"""Get a mail client to use"""
155
167
selected_client = self.get_user_option('mail_client')
174
186
"""Get a generic option - no special process, no default."""
175
187
return self._get_user_option(option_name)
189
def get_user_option_as_bool(self, option_name):
190
"""Get a generic option as a boolean - no special process, no default.
192
:return None if the option doesn't exist or its value can't be
193
interpreted as a boolean. Returns True or False otherwise.
195
s = self._get_user_option(option_name)
196
return ui.bool_from_string(s)
198
def get_user_option_as_list(self, option_name):
199
"""Get a generic option as a list - no special process, no default.
201
:return None if the option doesn't exist. Returns the value as a list
204
l = self._get_user_option(option_name)
205
if isinstance(l, (str, unicode)):
206
# A single value, most probably the user forgot the final ','
177
210
def gpg_signing_command(self):
178
211
"""What program should be used to sign signatures?"""
179
212
result = self._gpg_signing_command()
217
247
def username(self):
218
248
"""Return email-style username.
220
250
Something similar to 'Martin Pool <mbp@sourcefrog.net>'
222
252
$BZR_EMAIL can be set to override this (as well as the
223
253
deprecated $BZREMAIL), then
224
254
the concrete policy type is checked, and finally
225
255
$EMAIL is examined.
226
256
If none is found, a reasonable default is (hopefully)
229
259
TODO: Check it's reasonably well-formed.
231
261
v = os.environ.get('BZR_EMAIL')
328
def suppress_warning(self, warning):
329
"""Should the warning be suppressed or emitted.
331
:param warning: The name of the warning being tested.
333
:returns: True if the warning should be suppressed, False otherwise.
335
warnings = self.get_user_option_as_list('suppress_warnings')
336
if warnings is None or warning not in warnings:
299
342
class IniBasedConfig(Config):
300
343
"""A configuration policy that draws from ini files."""
345
def __init__(self, get_filename):
346
super(IniBasedConfig, self).__init__()
347
self._get_filename = get_filename
302
350
def _get_parser(self, file=None):
303
351
if self._parser is not None:
304
352
return self._parser
709
758
trace.warning('Value "%s" is masked by "%s" from'
710
759
' branch.conf', value, mask_value)
713
761
def _gpg_signing_command(self):
714
762
"""See Config.gpg_signing_command."""
715
763
return self._get_safe_value('_gpg_signing_command')
717
765
def __init__(self, branch):
718
766
super(BranchConfig, self).__init__()
719
767
self._location_config = None
720
768
self._branch_data_config = None
721
769
self._global_config = None
722
770
self.branch = branch
723
self.option_sources = (self._get_location_config,
771
self.option_sources = (self._get_location_config,
724
772
self._get_branch_data_config,
725
773
self._get_global_config)
813
861
return osutils.pathjoin(config_dir(), 'ignore')
865
"""Return the directory name to store crash files.
867
This doesn't implicitly create it.
869
On Windows it's in the config directory; elsewhere it's /var/crash
870
which may be monitored by apport. It can be overridden by
873
if sys.platform == 'win32':
874
return osutils.pathjoin(config_dir(), 'Crash')
876
# XXX: hardcoded in apport_python_hook.py; therefore here too -- mbp
878
return os.environ.get('APPORT_CRASH_DIR', '/var/crash')
882
# See http://standards.freedesktop.org/basedir-spec/latest/ar01s03.html
883
# Possibly this should be different on Windows?
884
e = os.environ.get('XDG_CACHE_DIR', None)
888
return os.path.expanduser('~/.cache')
816
891
def _auto_user_id():
817
892
"""Calculate automatic user identification.
917
992
# XXX: Really needs a better name, as this is not part of the tree! -- mbp 20080507
919
994
def __init__(self, branch):
920
# XXX: Really this should be asking the branch for its configuration
921
# data, rather than relying on a Transport, so that it can work
922
# more cleanly with a RemoteBranch that has no transport.
923
self._config = TransportConfig(branch._transport, 'branch.conf')
995
self._config = branch._get_config()
924
996
self.branch = branch
926
998
def _get_parser(self, file=None):
993
1064
section[option_name] = value
996
def get_credentials(self, scheme, host, port=None, user=None, path=None):
1067
def get_credentials(self, scheme, host, port=None, user=None, path=None,
997
1069
"""Returns the matching credentials from authentication.conf file.
999
1071
:param scheme: protocol
1005
1077
:param user: login (optional)
1007
1079
:param path: the absolute path on the server (optional)
1081
:param realm: the http authentication realm (optional)
1009
1083
:return: A dict containing the matching credentials or None.
1011
1085
- name: the section name of the credentials in the
1012
1086
authentication.conf file,
1013
- user: can't de different from the provided user if any,
1087
- user: can't be different from the provided user if any,
1088
- scheme: the server protocol,
1089
- host: the server address,
1090
- port: the server port (can be None),
1091
- path: the absolute server path (can be None),
1092
- realm: the http specific authentication realm (can be None),
1014
1093
- password: the decoded password, could be None if the credential
1015
1094
defines only the user
1016
1095
- verify_certificates: https specific, True if the server
1057
1136
if a_user is None:
1058
1137
# Can't find a user
1139
# Prepare a credentials dictionary with additional keys
1140
# for the credential providers
1060
1141
credentials = dict(name=auth_def_name,
1062
1148
password=auth_def.get('password', None),
1063
1149
verify_certificates=a_verify_certificates)
1150
# Decode the password in the credentials (or get one)
1064
1151
self.decode_password(credentials,
1065
1152
auth_def.get('password_encoding', None))
1066
1153
if 'auth' in debug.debug_flags:
1067
1154
trace.mutter("Using authentication section: %r", auth_def_name)
1157
if credentials is None:
1158
# No credentials were found in authentication.conf, try the fallback
1159
# credentials stores.
1160
credentials = credential_store_registry.get_fallback_credentials(
1161
scheme, host, port, user, path, realm)
1070
1163
return credentials
1072
1165
def set_credentials(self, name, host, user, scheme=None, password=None,
1073
port=None, path=None, verify_certificates=None):
1166
port=None, path=None, verify_certificates=None,
1074
1168
"""Set authentication credentials for a host.
1076
1170
Any existing credentials with matching scheme, host, port and path
1099
1194
values['path'] = path
1100
1195
if verify_certificates is not None:
1101
1196
values['verify_certificates'] = str(verify_certificates)
1197
if realm is not None:
1198
values['realm'] = realm
1102
1199
config = self._get_config()
1103
1200
for_deletion = []
1104
1201
for section, existing_values in config.items():
1105
for key in ('scheme', 'host', 'port', 'path'):
1202
for key in ('scheme', 'host', 'port', 'path', 'realm'):
1106
1203
if existing_values.get(key) != values.get(key):
1110
1207
config.update({name: values})
1113
def get_user(self, scheme, host, port=None,
1114
realm=None, path=None, prompt=None):
1210
def get_user(self, scheme, host, port=None, realm=None, path=None,
1211
prompt=None, ask=False, default=None):
1115
1212
"""Get a user from authentication file.
1117
1214
:param scheme: protocol
1125
1222
:param path: the absolute path on the server (optional)
1224
:param ask: Ask the user if there is no explicitly configured username
1227
:param default: The username returned if none is defined (optional).
1127
1229
:return: The found user.
1129
1231
credentials = self.get_credentials(scheme, host, port, user=None,
1232
path=path, realm=realm)
1131
1233
if credentials is not None:
1132
1234
user = credentials['user']
1240
# Create a default prompt suitable for most cases
1241
prompt = scheme.upper() + ' %(host)s username'
1242
# Special handling for optional fields in the prompt
1243
if port is not None:
1244
prompt_host = '%s:%d' % (host, port)
1247
user = ui.ui_factory.get_username(prompt, host=prompt_host)
1137
1252
def get_password(self, scheme, host, user, port=None,
1153
1268
:return: The found password or the one entered by the user.
1155
credentials = self.get_credentials(scheme, host, port, user, path)
1270
credentials = self.get_credentials(scheme, host, port, user, path,
1156
1272
if credentials is not None:
1157
1273
password = credentials['password']
1158
1274
if password is not None and scheme is 'ssh':
1191
1307
A credential store provides access to credentials via the password_encoding
1192
1308
field in authentication.conf sections.
1194
Except for stores provided by bzr itself,most stores are expected to be
1310
Except for stores provided by bzr itself, most stores are expected to be
1195
1311
provided by plugins that will therefore use
1196
1312
register_lazy(password_encoding, module_name, member_name, help=help,
1197
info=info) to install themselves.
1313
fallback=fallback) to install themselves.
1315
A fallback credential store is one that is queried if no credentials can be
1316
found via authentication.conf.
1200
1319
def get_credential_store(self, encoding=None):
1325
def is_fallback(self, name):
1326
"""Check if the named credentials store should be used as fallback."""
1327
return self.get_info(name)
1329
def get_fallback_credentials(self, scheme, host, port=None, user=None,
1330
path=None, realm=None):
1331
"""Request credentials from all fallback credentials stores.
1333
The first credentials store that can provide credentials wins.
1336
for name in self.keys():
1337
if not self.is_fallback(name):
1339
cs = self.get_credential_store(name)
1340
credentials = cs.get_credentials(scheme, host, port, user,
1342
if credentials is not None:
1343
# We found some credentials
1347
def register(self, key, obj, help=None, override_existing=False,
1349
"""Register a new object to a name.
1351
:param key: This is the key to use to request the object later.
1352
:param obj: The object to register.
1353
:param help: Help text for this entry. This may be a string or
1354
a callable. If it is a callable, it should take two
1355
parameters (registry, key): this registry and the key that
1356
the help was registered under.
1357
:param override_existing: Raise KeyErorr if False and something has
1358
already been registered for that key. If True, ignore if there
1359
is an existing key (always register the new value).
1360
:param fallback: Whether this credential store should be
1363
return super(CredentialStoreRegistry,
1364
self).register(key, obj, help, info=fallback,
1365
override_existing=override_existing)
1367
def register_lazy(self, key, module_name, member_name,
1368
help=None, override_existing=False,
1370
"""Register a new credential store to be loaded on request.
1372
:param module_name: The python path to the module. Such as 'os.path'.
1373
:param member_name: The member of the module to return. If empty or
1374
None, get() will return the module itself.
1375
:param help: Help text for this entry. This may be a string or
1377
:param override_existing: If True, replace the existing object
1378
with the new one. If False, if there is already something
1379
registered with the same key, raise a KeyError
1380
:param fallback: Whether this credential store should be
1383
return super(CredentialStoreRegistry, self).register_lazy(
1384
key, module_name, member_name, help,
1385
info=fallback, override_existing=override_existing)
1207
1388
credential_store_registry = CredentialStoreRegistry()
1211
1392
"""An abstract class to implement storage for credentials"""
1213
1394
def decode_password(self, credentials):
1214
"""Returns a password for the provided credentials in clear text."""
1395
"""Returns a clear text password for the provided credentials."""
1215
1396
raise NotImplementedError(self.decode_password)
1398
def get_credentials(self, scheme, host, port=None, user=None, path=None,
1400
"""Return the matching credentials from this credential store.
1402
This method is only called on fallback credential stores.
1404
raise NotImplementedError(self.get_credentials)
1218
1408
class PlainTextCredentialStore(CredentialStore):
1219
1409
"""Plain text credential store for the authentication.conf file."""
1231
1421
class BzrDirConfig(object):
1233
def __init__(self, transport):
1234
self._config = TransportConfig(transport, 'control.conf')
1423
def __init__(self, bzrdir):
1424
self._bzrdir = bzrdir
1425
self._config = bzrdir._get_config()
1236
1427
def set_default_stack_on(self, value):
1237
1428
"""Set the default stacking location.
1304
1499
configobj.setdefault(section, {})[name] = value
1305
1500
self._set_configobj(configobj)
1502
def _get_config_file(self):
1504
return StringIO(self._transport.get_bytes(self._filename))
1505
except errors.NoSuchFile:
1307
1508
def _get_configobj(self):
1309
return ConfigObj(self._transport.get(self._filename),
1311
except errors.NoSuchFile:
1312
return ConfigObj(encoding='utf-8')
1509
return ConfigObj(self._get_config_file(), encoding='utf-8')
1314
1511
def _set_configobj(self, configobj):
1315
1512
out_file = StringIO()