# HG changeset patch # User Mads Kiilerich # Date 2020-01-22 23:46:12 # Node ID 8f468d08f463e8f6c98c8129dd64e71db3c9bffd # Parent ed67d1df7125c777da9b4eebf6ed0f6e5b4aff55 # Parent 28fa94f56370199f078f94796cb2ac81c4513678 Merge stable diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -74,3 +74,4 @@ a18445b85d407294da0b7f1d8be3bedef5ffdea6 19086c5de05f4984d7a90cd31624c45dd893f6bb 0.4.0 da65398a62fff50f3d241796cbf17acdea2092ef 0.4.1 bfa0b0a814644f0af3f492d17a9ed169cc3b89fe 0.5.0 +d01a8e92936dbd62c76505432f60efba432e9397 0.5.1 diff --git a/CONTRIBUTORS b/CONTRIBUTORS --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -1,11 +1,12 @@ List of contributors to Kallithea project: + Thomas De Schampheleire 2014-2020 + Mads Kiilerich 2016-2020 Andrej Shadura 2012 2014-2017 2019 - Thomas De Schampheleire 2014-2019 Étienne Gilli 2015-2017 2019 - Mads Kiilerich 2016-2019 Allan Nordhøy 2017-2019 ssantos 2018-2019 + Adi Kriegisch 2019 Danni Randeris 2019 Edmund Wong 2019 Elizabeth Sherrock 2019 @@ -15,6 +16,7 @@ List of contributors to Kallithea projec Mateusz Mendel 2019 Nathan 2019 Oleksandr Shtalinberg 2019 + Private 2019 THANOS SIOURDAKIS 2019 Wolfgang Scherer 2019 Христо Станев 2019 diff --git a/development.ini b/development.ini --- a/development.ini +++ b/development.ini @@ -90,10 +90,12 @@ full_stack = true static_files = true ## Internationalization (see setup documentation for details) -## By default, the language requested by the browser is used if available. -#i18n.enabled = false -## Fallback language, empty for English (valid values are the names of subdirectories in kallithea/i18n): -i18n.lang = +## By default, the languages requested by the browser are used if available, with English as default. +## Set i18n.enabled=false to disable automatic language choice. +#i18n.enabled = true +## To Force a language, set i18n.enabled=false and specify the language in i18n.lang. +## Valid values are the names of subdirectories in kallithea/i18n with a LC_MESSAGES/kallithea.mo +#i18n.lang = en cache_dir = %(here)s/data index_dir = %(here)s/data/index diff --git a/docs/conf.py b/docs/conf.py --- a/docs/conf.py +++ b/docs/conf.py @@ -47,7 +47,7 @@ master_doc = 'index' # General information about the project. project = u'Kallithea' -copyright = u'2010-2019 by various authors, licensed as GPLv3.' +copyright = u'2010-2020 by various authors, licensed as GPLv3.' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/docs/setup.rst b/docs/setup.rst --- a/docs/setup.rst +++ b/docs/setup.rst @@ -80,13 +80,12 @@ The Kallithea web interface is automatic language, as indicated by the browser. Thus, different users may see the application in different languages. If the requested language is not available (because the translation file for that language does not yet exist or is -incomplete), the language specified in setting ``i18n.lang`` in the Kallithea -configuration file is used as fallback. If no fallback language is explicitly -specified, English is used. +incomplete), English is used. If you want to disable automatic language detection and instead configure a fixed language regardless of user preference, set ``i18n.enabled = false`` and -set ``i18n.lang`` to the desired language (or leave empty for English). +specify another language by setting ``i18n.lang`` in the Kallithea +configuration file. Using Kallithea with SSH @@ -562,7 +561,7 @@ that, you'll need to: ini = '/srv/kallithea/my.ini' from logging.config import fileConfig - fileConfig(ini) + fileConfig(ini, {'__file__': ini, 'here': '/srv/kallithea'}) from paste.deploy import loadapp application = loadapp('config:' + ini) @@ -578,7 +577,7 @@ that, you'll need to: ini = '/srv/kallithea/kallithea.ini' from logging.config import fileConfig - fileConfig(ini) + fileConfig(ini, {'__file__': ini, 'here': '/srv/kallithea'}) from paste.deploy import loadapp application = loadapp('config:' + ini) diff --git a/kallithea/alembic/env.py b/kallithea/alembic/env.py --- a/kallithea/alembic/env.py +++ b/kallithea/alembic/env.py @@ -15,6 +15,7 @@ # Alembic migration environment (configuration). import logging +import os from logging.config import fileConfig from alembic import context @@ -43,7 +44,9 @@ logging.getLogger('alembic').setLevel(lo # stamping during "kallithea-cli db-create"), config_file_name is not available, # and loggers are assumed to already have been configured. if config.config_file_name: - fileConfig(config.config_file_name, disable_existing_loggers=False) + fileConfig(config.config_file_name, + {'__file__': config.config_file_name, 'here': os.path.dirname(config.config_file_name)}, + disable_existing_loggers=False) def include_in_autogeneration(object, name, type, reflected, compare_to): diff --git a/kallithea/alembic/versions/4851d15bc437_db_migration_step_after_95c01895c006_.py b/kallithea/alembic/versions/4851d15bc437_db_migration_step_after_95c01895c006_.py --- a/kallithea/alembic/versions/4851d15bc437_db_migration_step_after_95c01895c006_.py +++ b/kallithea/alembic/versions/4851d15bc437_db_migration_step_after_95c01895c006_.py @@ -31,14 +31,20 @@ from alembic import op def upgrade(): - meta = sa.MetaData() - meta.reflect(bind=op.get_bind()) + pass + # The following upgrade step turned out to be a bad idea. A later step + # "d7ec25b66e47_ssh_drop_usk_public_key_idx_again" will remove the index + # again if it exists ... but we shouldn't even try to create it. - if not any(i.name == 'usk_public_key_idx' for i in meta.tables['user_ssh_keys'].indexes): - with op.batch_alter_table('user_ssh_keys', schema=None) as batch_op: - batch_op.create_index('usk_public_key_idx', ['public_key'], unique=False) + #meta = sa.MetaData() + #meta.reflect(bind=op.get_bind()) + + #if not any(i.name == 'usk_public_key_idx' for i in meta.tables['user_ssh_keys'].indexes): + # with op.batch_alter_table('user_ssh_keys', schema=None) as batch_op: + # batch_op.create_index('usk_public_key_idx', ['public_key'], unique=False) def downgrade(): - with op.batch_alter_table('user_ssh_keys', schema=None) as batch_op: - batch_op.drop_index('usk_public_key_idx') + if any(i.name == 'usk_public_key_idx' for i in meta.tables['user_ssh_keys'].indexes): + with op.batch_alter_table('user_ssh_keys', schema=None) as batch_op: + batch_op.drop_index('usk_public_key_idx') diff --git a/kallithea/alembic/versions/d7ec25b66e47_ssh_drop_usk_public_key_idx_again.py b/kallithea/alembic/versions/d7ec25b66e47_ssh_drop_usk_public_key_idx_again.py new file mode 100644 --- /dev/null +++ b/kallithea/alembic/versions/d7ec25b66e47_ssh_drop_usk_public_key_idx_again.py @@ -0,0 +1,43 @@ +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +"""ssh: drop usk_public_key_idx again + +Revision ID: d7ec25b66e47 +Revises: 4851d15bc437 +Create Date: 2019-12-29 15:33:10.982003 + +""" + +# The following opaque hexadecimal identifiers ("revisions") are used +# by Alembic to track this migration script and its relations to others. +revision = 'd7ec25b66e47' +down_revision = '4851d15bc437' +branch_labels = None +depends_on = None + +import sqlalchemy as sa +from alembic import op + + +def upgrade(): + meta = sa.MetaData() + meta.reflect(bind=op.get_bind()) + + if any(i.name == 'usk_public_key_idx' for i in meta.tables['user_ssh_keys'].indexes): + with op.batch_alter_table('user_ssh_keys', schema=None) as batch_op: + batch_op.drop_index('usk_public_key_idx') + + +def downgrade(): + pass diff --git a/kallithea/bin/kallithea_cli_base.py b/kallithea/bin/kallithea_cli_base.py --- a/kallithea/bin/kallithea_cli_base.py +++ b/kallithea/bin/kallithea_cli_base.py @@ -72,7 +72,8 @@ def register_command(config_file=False, path_to_ini_file = os.path.realpath(config_file) kallithea.CONFIG = paste.deploy.appconfig('config:' + path_to_ini_file) config_string = read_config(path_to_ini_file, strip_section_prefix=annotated.__name__) - logging.config.fileConfig(io.StringIO(config_string)) + logging.config.fileConfig(io.StringIO(config_string), + {'__file__': path_to_ini_file, 'here': os.path.dirname(path_to_ini_file)}) if config_file_initialize_app: kallithea.config.middleware.make_app_without_logging(kallithea.CONFIG.global_conf, **kallithea.CONFIG.local_conf) return annotated(*args, **kwargs) diff --git a/kallithea/bin/kallithea_cli_iis.py b/kallithea/bin/kallithea_cli_iis.py --- a/kallithea/bin/kallithea_cli_iis.py +++ b/kallithea/bin/kallithea_cli_iis.py @@ -33,7 +33,8 @@ import os def __ExtensionFactory__(): from paste.deploy import loadapp from logging.config import fileConfig - fileConfig('%(inifile)s') + fileConfig('%(inifile)s', {'__file__': '%(inifile)s', 'here': '%(inifiledir)s'}) + application = loadapp('config:%(inifile)s') def app(environ, start_response): @@ -75,6 +76,7 @@ def iis_install(virtualdir): with open(dispatchfile, 'w') as f: f.write(dispath_py_template % { 'inifile': config_file_abs.replace('\\', '\\\\'), + 'inifiledir': os.path.dirname(config_file_abs).replace('\\', '\\\\'), 'virtualdir': virtualdir, }) diff --git a/kallithea/bin/kallithea_cli_ssh.py b/kallithea/bin/kallithea_cli_ssh.py --- a/kallithea/bin/kallithea_cli_ssh.py +++ b/kallithea/bin/kallithea_cli_ssh.py @@ -24,7 +24,7 @@ import kallithea.bin.kallithea_cli_base from kallithea.lib.utils2 import str2bool from kallithea.lib.vcs.backends.git.ssh import GitSshHandler from kallithea.lib.vcs.backends.hg.ssh import MercurialSshHandler -from kallithea.model.ssh_key import SshKeyModel +from kallithea.model.ssh_key import SshKeyModel, SshKeyModelException log = logging.getLogger(__name__) @@ -82,5 +82,8 @@ def ssh_update_authorized_keys(): The file is usually maintained automatically, but this command will also re-write it. """ - - SshKeyModel().write_authorized_keys() + try: + SshKeyModel().write_authorized_keys() + except SshKeyModelException as e: + sys.stderr.write("%s\n" % e) + sys.exit(1) diff --git a/kallithea/config/app_cfg.py b/kallithea/config/app_cfg.py --- a/kallithea/config/app_cfg.py +++ b/kallithea/config/app_cfg.py @@ -98,6 +98,11 @@ class KallitheaAppConfig(AppConfig): # Disable transaction manager -- currently Kallithea takes care of transactions itself self['tm.enabled'] = False + # Set the i18n source language so TG doesn't search beyond 'en' in Accept-Language. + # Don't force the default here if configuration force something else. + if not self.get('i18n.lang'): + self['i18n.lang'] = 'en' + base_config = KallitheaAppConfig() diff --git a/kallithea/config/middleware.py b/kallithea/config/middleware.py --- a/kallithea/config/middleware.py +++ b/kallithea/config/middleware.py @@ -13,8 +13,6 @@ # along with this program. If not, see . """WSGI middleware initialization for the Kallithea application.""" -import logging.config - from kallithea.config.app_cfg import base_config from kallithea.config.environment import load_environment @@ -49,5 +47,4 @@ def make_app(global_conf, full_stack=Tru ``app_conf`` contains all the application-specific settings (those defined under ``[app:main]``. """ - logging.config.fileConfig(global_conf['__file__']) return make_app_without_logging(global_conf, full_stack=full_stack, **app_conf) diff --git a/kallithea/controllers/admin/my_account.py b/kallithea/controllers/admin/my_account.py --- a/kallithea/controllers/admin/my_account.py +++ b/kallithea/controllers/admin/my_account.py @@ -285,9 +285,9 @@ class MyAccountController(BaseController @IfSshEnabled def my_account_ssh_keys_delete(self): - public_key = request.POST.get('del_public_key') + fingerprint = request.POST.get('del_public_key_fingerprint') try: - SshKeyModel().delete(public_key, request.authuser.user_id) + SshKeyModel().delete(fingerprint, request.authuser.user_id) Session().commit() SshKeyModel().write_authorized_keys() h.flash(_("SSH key successfully deleted"), category='success') diff --git a/kallithea/controllers/admin/users.py b/kallithea/controllers/admin/users.py --- a/kallithea/controllers/admin/users.py +++ b/kallithea/controllers/admin/users.py @@ -460,9 +460,9 @@ class UsersController(BaseController): def ssh_keys_delete(self, id): c.user = self._get_user_or_raise_if_default(id) - public_key = request.POST.get('del_public_key') + fingerprint = request.POST.get('del_public_key_fingerprint') try: - SshKeyModel().delete(public_key, c.user.user_id) + SshKeyModel().delete(fingerprint, c.user.user_id) Session().commit() SshKeyModel().write_authorized_keys() h.flash(_("SSH key successfully deleted"), category='success') diff --git a/kallithea/controllers/login.py b/kallithea/controllers/login.py --- a/kallithea/controllers/login.py +++ b/kallithea/controllers/login.py @@ -210,12 +210,10 @@ class LoginController(BaseController): # The template needs the email address outside of the form. c.email = request.params.get('email') - + c.timestamp = request.params.get('timestamp') or '' + c.token = request.params.get('token') or '' if not request.POST: - return htmlfill.render( - render('/password_reset_confirmation.html'), - defaults=dict(request.params), - encoding='UTF-8') + return render('/password_reset_confirmation.html') form = PasswordResetConfirmationForm()() try: diff --git a/kallithea/i18n/en/LC_MESSAGES/kallithea.mo b/kallithea/i18n/en/LC_MESSAGES/kallithea.mo new file mode 100644 index 0000000000000000000000000000000000000000..babcfcd322ba8631ee98ce633dbe7dd029faea32 GIT binary patch literal 20 Oc$}NcB6N=d4FCWf1_6oy diff --git a/kallithea/lib/auth.py b/kallithea/lib/auth.py --- a/kallithea/lib/auth.py +++ b/kallithea/lib/auth.py @@ -28,6 +28,7 @@ import hashlib import itertools import logging import os +import string import ipaddr from decorator import decorator @@ -109,8 +110,9 @@ def check_password(password, hashed): :param password: password :param hashed: password in hashed form """ - - if is_windows: + # sha256 hashes will always be 64 hex chars + # bcrypt hashes will always contain $ (and be shorter) + if is_windows or len(hashed) == 64 and all(x in string.hexdigits for x in hashed): return hashlib.sha256(safe_bytes(password)).hexdigest() == hashed elif is_unix: import bcrypt diff --git a/kallithea/lib/hooks.py b/kallithea/lib/hooks.py --- a/kallithea/lib/hooks.py +++ b/kallithea/lib/hooks.py @@ -26,6 +26,7 @@ Original author and date, and relevant c """ import os +import sys import time import mercurial.scmutil @@ -33,7 +34,7 @@ import mercurial.scmutil from kallithea.lib import helpers as h from kallithea.lib.exceptions import UserCreationError from kallithea.lib.utils import action_logger, make_ui -from kallithea.lib.utils2 import ascii_str, get_hook_environment, safe_bytes, safe_str, safe_unicode +from kallithea.lib.utils2 import HookEnvironmentError, ascii_str, get_hook_environment, safe_bytes, safe_str, safe_unicode from kallithea.lib.vcs.backends.base import EmptyChangeset from kallithea.model.db import Repository, User @@ -333,7 +334,11 @@ def handle_git_pre_receive(repo_path, gi def handle_git_post_receive(repo_path, git_stdin_lines): """Called from Git post-receive hook""" - baseui, repo = _hook_environment(repo_path) + try: + baseui, repo = _hook_environment(repo_path) + except HookEnvironmentError as e: + sys.stderr.write("Skipping Kallithea Git post-recieve hook %r.\nGit was apparently not invoked by Kallithea: %s\n" % (sys.argv[0], e)) + return 0 # the post push hook should never use the cached instance scm_repo = repo.scm_instance_no_cache() diff --git a/kallithea/lib/paster_commands/template.ini.mako b/kallithea/lib/paster_commands/template.ini.mako --- a/kallithea/lib/paster_commands/template.ini.mako +++ b/kallithea/lib/paster_commands/template.ini.mako @@ -185,10 +185,12 @@ full_stack = true static_files = true <%text>## Internationalization (see setup documentation for details) -<%text>## By default, the language requested by the browser is used if available. -#i18n.enabled = false -<%text>## Fallback language, empty for English (valid values are the names of subdirectories in kallithea/i18n): -i18n.lang = +<%text>## By default, the languages requested by the browser are used if available, with English as default. +<%text>## Set i18n.enabled=false to disable automatic language choice. +#i18n.enabled = true +<%text>## To Force a language, set i18n.enabled=false and specify the language in i18n.lang. +<%text>## Valid values are the names of subdirectories in kallithea/i18n with a LC_MESSAGES/kallithea.mo +#i18n.lang = en cache_dir = %(here)s/data index_dir = %(here)s/data/index diff --git a/kallithea/lib/ssh.py b/kallithea/lib/ssh.py --- a/kallithea/lib/ssh.py +++ b/kallithea/lib/ssh.py @@ -48,7 +48,7 @@ def parse_pub_key(ssh_key): >>> parse_pub_key('''AAAAB3NzaC1yc2EAAAALVGhpcyBpcyBmYWtlIQ''') Traceback (most recent call last): ... - SshKeyParseError: Incorrect SSH key - it must have both a key type and a base64 part + SshKeyParseError: Incorrect SSH key - it must have both a key type and a base64 part, like 'ssh-rsa ASRNeaZu4FA...xlJp=' >>> parse_pub_key('''abc AAAAB3NzaC1yc2EAAAALVGhpcyBpcyBmYWtlIQ''') Traceback (most recent call last): ... @@ -76,7 +76,7 @@ def parse_pub_key(ssh_key): parts = ssh_key.split(None, 2) if len(parts) < 2: - raise SshKeyParseError(_("Incorrect SSH key - it must have both a key type and a base64 part")) + raise SshKeyParseError(_("Incorrect SSH key - it must have both a key type and a base64 part, like 'ssh-rsa ASRNeaZu4FA...xlJp='")) keytype, keyvalue, comment = (parts + [''])[:3] if keytype not in ('ssh-rsa', 'ssh-dss', 'ssh-ed25519'): @@ -99,6 +99,18 @@ def parse_pub_key(ssh_key): SSH_OPTIONS = 'no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding' +def _safe_check(s, rec = re.compile('^[a-zA-Z0-9+/]+={0,2}$')): + """Return true if s really has the right content for base64 encoding and only contains safe characters + >>> _safe_check('asdf') + True + >>> _safe_check('as df') + False + >>> _safe_check('AAAAB3NzaC1yc2EAAAALVGhpcyBpcyBmYWtlIQ==') + True + """ + return rec.match(s) is not None + + def authorized_keys_line(kallithea_cli_path, config_file, key): """ Return a line as it would appear in .authorized_keys @@ -116,6 +128,8 @@ def authorized_keys_line(kallithea_cli_p return '# Invalid Kallithea SSH key: %s %s\n' % (key.user.user_id, key.user_ssh_key_id) base64_key = ascii_str(base64.b64encode(key_bytes)) assert '\n' not in base64_key + if not _safe_check(base64_key): + return '# Invalid Kallithea SSH key - bad base64 encoding: %s %s\n' % (key.user.user_id, key.user_ssh_key_id) return '%s,command="%s ssh-serve -c %s %s %s" %s %s\n' % ( SSH_OPTIONS, kallithea_cli_path, config_file, key.user.user_id, key.user_ssh_key_id, diff --git a/kallithea/lib/utils2.py b/kallithea/lib/utils2.py --- a/kallithea/lib/utils2.py +++ b/kallithea/lib/utils2.py @@ -430,6 +430,9 @@ def obfuscate_url_pw(engine): return str(_url) +class HookEnvironmentError(Exception): pass + + def get_hook_environment(): """ Get hook context by deserializing the global KALLITHEA_EXTRAS environment @@ -441,15 +444,16 @@ def get_hook_environment(): """ try: - extras = json.loads(os.environ['KALLITHEA_EXTRAS']) + kallithea_extras = os.environ['KALLITHEA_EXTRAS'] except KeyError: - raise Exception("Environment variable KALLITHEA_EXTRAS not found") + raise HookEnvironmentError("Environment variable KALLITHEA_EXTRAS not found") + extras = json.loads(kallithea_extras) try: - for k in ['username', 'repository', 'scm', 'action', 'ip']: + for k in ['username', 'repository', 'scm', 'action', 'ip', 'config']: extras[k] except KeyError: - raise Exception('Missing key %s in KALLITHEA_EXTRAS %s' % (k, extras)) + raise HookEnvironmentError('Missing key %s in KALLITHEA_EXTRAS %s' % (k, extras)) return AttributeDict(extras) diff --git a/kallithea/model/db.py b/kallithea/model/db.py --- a/kallithea/model/db.py +++ b/kallithea/model/db.py @@ -2523,7 +2523,6 @@ class Gist(Base, BaseDbModel): class UserSshKeys(Base, BaseDbModel): __tablename__ = 'user_ssh_keys' __table_args__ = ( - Index('usk_public_key_idx', 'public_key'), Index('usk_fingerprint_idx', 'fingerprint'), UniqueConstraint('fingerprint'), _table_args_default_dict diff --git a/kallithea/model/ssh_key.py b/kallithea/model/ssh_key.py --- a/kallithea/model/ssh_key.py +++ b/kallithea/model/ssh_key.py @@ -30,6 +30,7 @@ from tg.i18n import ugettext as _ from kallithea.lib import ssh from kallithea.lib.utils2 import str2bool +from kallithea.lib.vcs.exceptions import RepositoryError from kallithea.model.db import User, UserSshKeys from kallithea.model.meta import Session @@ -37,7 +38,7 @@ from kallithea.model.meta import Session log = logging.getLogger(__name__) -class SshKeyModelException(Exception): +class SshKeyModelException(RepositoryError): """Exception raised by SshKeyModel methods to report errors""" @@ -72,21 +73,19 @@ class SshKeyModel(object): return new_ssh_key - def delete(self, public_key, user=None): + def delete(self, fingerprint, user): """ - Deletes given public_key, if user is set it also filters the object for - deletion by given user. + Deletes ssh key with given fingerprint for the given user. Will raise SshKeyModelException on errors """ - ssh_key = UserSshKeys.query().filter(UserSshKeys._public_key == public_key) + ssh_key = UserSshKeys.query().filter(UserSshKeys.fingerprint == fingerprint) - if user: - user = User.guess_instance(user) - ssh_key = ssh_key.filter(UserSshKeys.user_id == user.user_id) + user = User.guess_instance(user) + ssh_key = ssh_key.filter(UserSshKeys.user_id == user.user_id) ssh_key = ssh_key.scalar() if ssh_key is None: - raise SshKeyModelException(_('SSH key %r not found') % public_key) + raise SshKeyModelException(_('SSH key with fingerprint %r found') % fingerprint) Session().delete(ssh_key) def get_ssh_keys(self, user): @@ -116,7 +115,7 @@ class SshKeyModel(object): # Now, test that the directory is or was created in a readable way by previous. if not (os.path.isdir(authorized_keys_dir) and os.access(authorized_keys_dir, os.W_OK)): - raise Exception("Directory of authorized_keys cannot be written to so authorized_keys file %s cannot be written" % (authorized_keys)) + raise SshKeyModelException("Directory of authorized_keys cannot be written to so authorized_keys file %s cannot be written" % (authorized_keys)) # Make sure we don't overwrite a key file with important content if os.path.exists(authorized_keys): @@ -127,10 +126,11 @@ class SshKeyModel(object): elif ssh.SSH_OPTIONS in l and ' ssh-serve ' in l: pass # Kallithea entries are ok to overwrite else: - raise Exception("Safety check failed, found %r in %s - please review and remove it" % (l.strip(), authorized_keys)) + raise SshKeyModelException("Safety check failed, found %r line in %s - please remove it if Kallithea should manage the file" % (l.strip(), authorized_keys)) fh, tmp_authorized_keys = tempfile.mkstemp('.authorized_keys', dir=os.path.dirname(authorized_keys)) with os.fdopen(fh, 'w') as f: + f.write("# WARNING: This .ssh/authorized_keys file is managed by Kallithea. Manual editing or adding new entries will make Kallithea back off.\n") for key in UserSshKeys.query().join(UserSshKeys.user).filter(User.active == True): f.write(ssh.authorized_keys_line(kallithea_cli_path, config['__file__'], key)) os.chmod(tmp_authorized_keys, stat.S_IRUSR | stat.S_IWUSR) diff --git a/kallithea/templates/about.html b/kallithea/templates/about.html --- a/kallithea/templates/about.html +++ b/kallithea/templates/about.html @@ -24,12 +24,13 @@ necessarily limited to the following:

    -
  • Copyright © 2012–2019, Mads Kiilerich
  • +
  • Copyright © 2012–2020, Mads Kiilerich
  • +
  • Copyright © 2014–2020, Thomas De Schampheleire
  • Copyright © 2012, 2014–2017, 2019, Andrej Shadura
  • -
  • Copyright © 2014–2019, Thomas De Schampheleire
  • Copyright © 2015–2017, 2019, Étienne Gilli
  • Copyright © 2017–2019, Allan Nordhøy
  • Copyright © 2018–2019, ssantos
  • +
  • Copyright © 2019, Adi Kriegisch
  • Copyright © 2019, Danni Randeris
  • Copyright © 2019, Edmund Wong
  • Copyright © 2019, Elizabeth Sherrock
  • @@ -39,6 +40,7 @@
  • Copyright © 2019, Mateusz Mendel
  • Copyright © 2019, Nathan
  • Copyright © 2019, Oleksandr Shtalinberg
  • +
  • Copyright © 2019, Private
  • Copyright © 2019, THANOS SIOURDAKIS
  • Copyright © 2019, Wolfgang Scherer
  • Copyright © 2019, Христо Станев
  • diff --git a/kallithea/templates/admin/my_account/my_account_ssh_keys.html b/kallithea/templates/admin/my_account/my_account_ssh_keys.html --- a/kallithea/templates/admin/my_account/my_account_ssh_keys.html +++ b/kallithea/templates/admin/my_account/my_account_ssh_keys.html @@ -23,7 +23,7 @@ ${h.form(url('my_account_ssh_keys_delete'))} - ${h.hidden('del_public_key', ssh_key.public_key)} + ${h.hidden('del_public_key_fingerprint', ssh_key.fingerprint)}