Exemple #1
0
    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)
Exemple #2
0
    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()
Exemple #3
0
    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
Exemple #4
0
    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)
Exemple #5
0
    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)
Exemple #6
0
    def serve(self, user_id, key_id, client_ip):
        """Verify basic sanity of the repository, and that the user is
        valid and has access - then serve the native VCS protocol for
        repository access."""
        dbuser = User.get(user_id)
        if dbuser is None:
            self.exit('User %r not found' % user_id)
        self.authuser = AuthUser.make(dbuser=dbuser, ip_addr=client_ip)
        log.info('Authorized user %s from SSH %s trusting user id %s and key id %s for %r', dbuser, client_ip, user_id, key_id, self.repo_name)
        if self.authuser is None: # not ok ... but already kind of authenticated by SSH ... but not really not authorized ...
            self.exit('User %s from %s cannot be authorized' % (dbuser.username, client_ip))

        ssh_key = UserSshKeys.get(key_id)
        if ssh_key is None:
            self.exit('SSH key %r not found' % key_id)
        ssh_key.last_seen = datetime.datetime.now()
        Session().commit()

        if HasPermissionAnyMiddleware('repository.write',
                                      'repository.admin')(self.authuser, self.repo_name):
            self.allow_push = True
        elif HasPermissionAnyMiddleware('repository.read')(self.authuser, self.repo_name):
            self.allow_push = False
        else:
            self.exit('Access to %r denied' % self.repo_name)

        self.db_repo = Repository.get_by_repo_name(self.repo_name)
        if self.db_repo is None:
            self.exit("Repository '%s' not found" % self.repo_name)
        assert self.db_repo.repo_name == self.repo_name

        # Set global hook environment up for 'push' actions.
        # If pull actions should be served, the actual hook invocation will be
        # hardcoded to 'pull' when log_pull_action is invoked (directly on Git,
        # or through the Mercurial 'outgoing' hook).
        # For push actions, the action in global hook environment is used (in
        # handle_git_post_receive when it is called as Git post-receive hook,
        # or in log_push_action through the Mercurial 'changegroup' hook).
        set_hook_environment(self.authuser.username, client_ip, self.repo_name, self.vcs_type, 'push')
        return self._serve()
Exemple #7
0
 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
Exemple #8
0
 def test_fingerprint_generation(self):
     key_model = UserSshKeys()
     key_model.public_key = public_key
     expected = 'Ke3oUCNJM87P0jJTb3D+e3shjceP2CqMpQKVd75E9I8'
     assert expected == key_model.fingerprint