def test_my_account_remove_ssh_key(self): description = '' public_key = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC6Ycnc2oUZHQnQwuqgZqTTdMDZD7ataf3JM7oG2Fw8JR6cdmz4QZLe5mfDwaFwG2pWHLRpVqzfrD/Pn3rIO++bgCJH5ydczrl1WScfryV1hYMJ/4EzLGM657J1/q5EI+b9SntKjf4ax+KP322L0TNQGbZUHLbfG2MwHMrYBQpHUQ== me@localhost' fingerprint = 'Ke3oUCNJM87P0jJTb3D+e3shjceP2CqMpQKVd75E9I8' self.log_user(base.TEST_USER_REGULAR2_LOGIN, base.TEST_USER_REGULAR2_PASS) response = self.app.post( base.url('my_account_ssh_keys'), { 'description': description, 'public_key': public_key, '_session_csrf_secret_token': self.session_csrf_secret_token() }) self.checkSessionFlash(response, 'SSH key %s successfully added' % fingerprint) response.follow() user_id = response.session['authuser']['user_id'] ssh_key = UserSshKeys.query().filter( UserSshKeys.user_id == user_id).one() assert ssh_key.description == 'me@localhost' response = self.app.post( base.url('my_account_ssh_keys_delete'), { 'del_public_key_fingerprint': ssh_key.fingerprint, '_session_csrf_secret_token': self.session_csrf_secret_token() }) self.checkSessionFlash(response, 'SSH key successfully deleted') keys = UserSshKeys.query().all() assert 0 == len(keys)
def create(self, user, description, public_key): """ :param user: user or user_id :param description: description of SshKey :param publickey: public key text Will raise SshKeyModelException on errors """ try: keytype, _pub, comment = ssh.parse_pub_key(public_key) except ssh.SshKeyParseError as e: raise SshKeyModelException(_('SSH key %r is invalid: %s') % (public_key, e.args[0])) if not description.strip(): description = comment.strip() user = User.guess_instance(user) new_ssh_key = UserSshKeys() new_ssh_key.user_id = user.user_id new_ssh_key.description = description new_ssh_key.public_key = public_key for ssh_key in UserSshKeys.query().filter(UserSshKeys.fingerprint == new_ssh_key.fingerprint).all(): raise SshKeyModelException(_('SSH key %s is already used by %s') % (new_ssh_key.fingerprint, ssh_key.user.username)) Session().add(new_ssh_key) return new_ssh_key
def test_add_ssh_key(self): description = 'something' public_key = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC6Ycnc2oUZHQnQwuqgZqTTdMDZD7ataf3JM7oG2Fw8JR6cdmz4QZLe5mfDwaFwG2pWHLRpVqzfrD/Pn3rIO++bgCJH5ydczrl1WScfryV1hYMJ/4EzLGM657J1/q5EI+b9SntKjf4ax+KP322L0TNQGbZUHLbfG2MwHMrYBQpHUQ== me@localhost' fingerprint = 'Ke3oUCNJM87P0jJTb3D+e3shjceP2CqMpQKVd75E9I8' self.log_user() user = User.get_by_username(base.TEST_USER_REGULAR_LOGIN) user_id = user.user_id response = self.app.post( base.url('edit_user_ssh_keys', id=user_id), { 'description': description, 'public_key': public_key, '_session_csrf_secret_token': self.session_csrf_secret_token() }) self.checkSessionFlash(response, 'SSH key %s successfully added' % fingerprint) response = response.follow() response.mustcontain(fingerprint) ssh_key = UserSshKeys.query().filter( UserSshKeys.user_id == user_id).one() assert ssh_key.fingerprint == fingerprint assert ssh_key.description == description Session().delete(ssh_key) Session().commit()
def delete(self, fingerprint, user): """ Deletes ssh key with given fingerprint for the given user. Will raise SshKeyModelException on errors """ ssh_key = UserSshKeys.query().filter(UserSshKeys.fingerprint == fingerprint) 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 with fingerprint %r found') % fingerprint) Session().delete(ssh_key)
def write_authorized_keys(self): if not str2bool(config.get('ssh_enabled', False)): log.error("Will not write SSH authorized_keys file - ssh_enabled is not configured") return authorized_keys = config.get('ssh_authorized_keys') kallithea_cli_path = config.get('kallithea_cli_path', 'kallithea-cli') if not authorized_keys: log.error('Cannot write SSH authorized_keys file - ssh_authorized_keys is not configured') return log.info('Writing %s', authorized_keys) authorized_keys_dir = os.path.dirname(authorized_keys) try: os.makedirs(authorized_keys_dir) os.chmod(authorized_keys_dir, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) # ~/.ssh/ must be 0700 except OSError as exception: if exception.errno != errno.EEXIST: raise # 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 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): with open(authorized_keys) as f: for l in f: if not l.strip() or l.startswith('#'): pass # accept empty lines and comments elif ssh.SSH_OPTIONS in l and ' ssh-serve ' in l: pass # Kallithea entries are ok to overwrite else: 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) # Note: simple overwrite / rename isn't enough to replace the file on Windows os.replace(tmp_authorized_keys, authorized_keys)
def get_ssh_keys(self, user): user = User.guess_instance(user) user_ssh_keys = UserSshKeys.query() \ .filter(UserSshKeys.user_id == user.user_id).all() return user_ssh_keys