Beispiel #1
0
def update_permissions(id, **kwargs):
    allowed_permissions = ['can_create_users', 'can_create_repositories']
    for key in kwargs.keys():
        if key not in allowed_permissions:
            raise KeyError("You can only change these permissions: " +
                           ', '.join(allowed_permissions))
    db.execute(users.update().values(**kwargs).where(users.c.user_id == id))
Beispiel #2
0
def create_repo(name, owner_id):
    (name, path) = format_name(name)

    # TODO: a synchronization problem exists here when:
    #    - the repository already exists, but doesn't appear in the DB
    #    - the repository is missing, but appears in the DB
    # If one of these conditions exists, what do we do?
    #
    # For now:
    #    - if it exists in FS but not DB, populate DB
    #    - if it exists in DB but not FS, initialize repo in FS
    #
    # Basically, we naively chug on through and initialize all uninitialized
    # pieces without throwing exceptions.  I'm not sure this is the best
    # policy, as desyncs here may indicate further consistency issues, and
    # ignoring them may hamper debugging.

    # error out if the repository already exists in both FS and DB
    repo = get_repo(name)
    if os.path.isdir(path) and repo:
        raise KeyError("Repository {0} already exists.".format(name))

    # if no FS directory exists, initialize the repo /w subprocess'ed git
    if not os.path.isdir(path):
        # TODO: can we use a native Python library here instead of subprocess?
        # TODO: do we need to insure git is installed properly somehow?
        import subprocess
        cmd = ['git', 'init', '--bare', path]
        proc = subprocess.Popen(args=cmd,
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE)
        (stdout, stderr) = proc.communicate()
        if proc.returncode != 0:
            raise Exception("git init {0} failed: {1}".format(path, stderr))

    # insert repository row
    s = repos.insert().values(repository_name=name)
    result = db.execute(s)
    repo_id = result.inserted_primary_key[0]

    # insert default owner ACL
    # TODO: Shouldn't this be in the same transaction as above?!
    #       Theoretically a failure here could be unrecoverable.
    s = repo_acls.insert().values(
        user_id=owner_id,
        repository_id=repo_id,
        is_owner=True,
        can_write=True,
        can_rewind=True,
        can_read=True,
        can_create_tag=True,
        can_modify_tag=True,
    )
    result = db.execute(s)

    # make sure all hooks are up-to-date
    update_hooks(name)

    return repo_id
Beispiel #3
0
def create_repo(name, owner_id):
    (name, path) = format_name(name)

    # TODO: a synchronization problem exists here when:
    #    - the repository already exists, but doesn't appear in the DB
    #    - the repository is missing, but appears in the DB
    # If one of these conditions exists, what do we do?
    #
    # For now:
    #    - if it exists in FS but not DB, populate DB
    #    - if it exists in DB but not FS, initialize repo in FS
    #
    # Basically, we naively chug on through and initialize all uninitialized
    # pieces without throwing exceptions.  I'm not sure this is the best
    # policy, as desyncs here may indicate further consistency issues, and
    # ignoring them may hamper debugging.

    # error out if the repository already exists in both FS and DB
    repo = get_repo(name)
    if os.path.isdir(path) and repo:
        raise KeyError("Repository {0} already exists.".format(name))

    # if no FS directory exists, initialize the repo /w subprocess'ed git
    if not os.path.isdir(path):
        # TODO: can we use a native Python library here instead of subprocess?
        # TODO: do we need to insure git is installed properly somehow?
        import subprocess

        cmd = ["git", "init", "--bare", path]
        proc = subprocess.Popen(args=cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        (stdout, stderr) = proc.communicate()
        if proc.returncode != 0:
            raise Exception("git init {0} failed: {1}".format(path, stderr))

    # insert repository row
    s = repos.insert().values(repository_name=name)
    result = db.execute(s)
    repo_id = result.inserted_primary_key[0]

    # insert default owner ACL
    # TODO: Shouldn't this be in the same transaction as above?!
    #       Theoretically a failure here could be unrecoverable.
    s = repo_acls.insert().values(
        user_id=owner_id,
        repository_id=repo_id,
        is_owner=True,
        can_write=True,
        can_rewind=True,
        can_read=True,
        can_create_tag=True,
        can_modify_tag=True,
    )
    result = db.execute(s)

    # make sure all hooks are up-to-date
    update_hooks(name)

    return repo_id
Beispiel #4
0
def update_permissions(id, **kwargs):
    allowed_permissions = [
        'can_create_users',
        'can_create_repositories'
    ]
    for key in kwargs.keys():
        if key not in allowed_permissions:
            raise KeyError("You can only change these permissions: " +
                ', '.join(allowed_permissions))
    db.execute(users
        .update()
        .values(**kwargs)
        .where(users.c.user_id == id))
Beispiel #5
0
def delete_repo_by_id(id):
    repo = get_repo_by_id(id)
    if not repo:
        raise KeyError("Invalid repository ID")
    (name, path) = format_name(repo['repository_name'])
    import shutil
    shutil.rmtree(path)
    # TODO: Shouldn't these two be in the same transaction?
    #       How do we implement transactions in SQLAlchemy?
    s = repos.delete().where(repos.c.repository_id == id)
    result = db.execute(s)
    s = repo_acls.delete().where(repo_acls.c.repository_id == id)
    result = db.execute(s)
Beispiel #6
0
def update_repo_acls(repo_id, user_id, **kwargs):
    # check if we need to do an update
    # TODO: This behavior is ANYTHING except transaction-safe...
    s = select([repo_acls]).where((repo_acls.c.repository_id == repo_id) & (repo_acls.c.user_id == user_id))
    acl = db.execute(s).fetchone()
    if not acl:
        s = repo_acls.insert().values(repository_id=repo_id, user_id=user_id, **kwargs)
    else:
        s = (
            repo_acls.update()
            .values(**kwargs)
            .where((repo_acls.c.repository_id == repo_id) & (repo_acls.c.user_id == user_id))
        )
    db.execute(s)
Beispiel #7
0
def delete_repo_by_id(id):
    repo = get_repo_by_id(id)
    if not repo:
        raise KeyError("Invalid repository ID")
    (name, path) = format_name(repo["repository_name"])
    import shutil

    shutil.rmtree(path)
    # TODO: Shouldn't these two be in the same transaction?
    #       How do we implement transactions in SQLAlchemy?
    s = repos.delete().where(repos.c.repository_id == id)
    result = db.execute(s)
    s = repo_acls.delete().where(repo_acls.c.repository_id == id)
    result = db.execute(s)
Beispiel #8
0
def update_repo_acls(repo_id, user_id, **kwargs):
    # check if we need to do an update
    # TODO: This behavior is ANYTHING except transaction-safe...
    s = select([repo_acls]).where((repo_acls.c.repository_id == repo_id)
                                  & (repo_acls.c.user_id == user_id))
    acl = db.execute(s).fetchone()
    if not acl:
        s = repo_acls.insert().values(repository_id=repo_id,
                                      user_id=user_id,
                                      **kwargs)
    else:
        s = repo_acls.update().values(
            **kwargs).where((repo_acls.c.repository_id == repo_id)
                            & (repo_acls.c.user_id == user_id))
    db.execute(s)
Beispiel #9
0
def change_password(name, password, old_password=None):
    """
    Changes the target user's password to the password given by
    ``password``.  If ``old_password`` is given but does not match
    the user's current password, a ValueError is raised.
    """
    if not get_user(name):
        raise KeyError("User '{0}' not found".format(name))
    if old_password is not None:
        if not verify_user(name, password):
            raise ValueError("Old password was incorrect.")
    salt = generate_salt()
    hash = hash_password(password, salt)
    db.execute(users.update().values(
        pass_salt=salt, pass_hash=hash).where(users.c.user_name == name))
Beispiel #10
0
def regenerate_authorized_keys():
    # do as much validaton as possible
    home = os.getenv('HOME', None)
    if home is None:
        raise ValueError("HOME environment variable should be set!")

    # perform SQL query
    s = select([keys, users]).where(keys.c.user_id == users.c.user_id)
    result = db.execute(s)

    # attempt to locate our SSH wrapper
    from distutils.spawn import find_executable
    wrapper_path = find_executable('gitzebo-ssh-wrapper')

    # make sure ssh directory exists
    target_dir = os.path.join(home, '.ssh')
    try:
        os.makedirs(target_dir)
        os.chmod(target_dir, 0700)
    except OSError as e:
        if e.errno == errno.EEXIST and os.path.isdir(target_dir):
            pass
        else:
            raise

    # write resultset to file
    target_path = os.path.join(target_dir, 'authorized_keys')
    # TODO: eventually allow for DSA keys, too?
    venv = ''
    venv_dir = os.getenv('VIRTUAL_ENV', None)
    if venv_dir:
        venv = ' VIRTUAL_ENV=' + venv_dir
    with open(target_path, 'w') as f:
        for row in db.execute(s):
            f.write('command="{wrapper} USERNAME={username}{venv}",'
                'no-port-forwarding,'
                'no-X11-forwarding,'
                'no-agent-forwarding,'
                'no-pty'
                ' ssh-rsa {key}\n'.format(
                    wrapper=wrapper_path,
                    username=row['user_name'],
                    key=row['public_key'],
                    venv=venv, # virtualenv spec, if applicable
                )
            )
Beispiel #11
0
def change_password(name, password, old_password=None):
    """
    Changes the target user's password to the password given by
    ``password``.  If ``old_password`` is given but does not match
    the user's current password, a ValueError is raised.
    """
    if not get_user(name):
        raise KeyError("User '{0}' not found".format(name))
    if old_password is not None:
        if not verify_user(name, password):
            raise ValueError("Old password was incorrect.")
    salt = generate_salt()
    hash = hash_password(password, salt)
    db.execute(users
        .update()
        .values(pass_salt=salt, pass_hash=hash)
        .where(users.c.user_name == name))
Beispiel #12
0
def regenerate_authorized_keys():
    # do as much validaton as possible
    home = os.getenv('HOME', None)
    if home is None:
        raise ValueError("HOME environment variable should be set!")

    # perform SQL query
    s = select([keys, users]).where(keys.c.user_id == users.c.user_id)
    result = db.execute(s)

    # attempt to locate our SSH wrapper
    from distutils.spawn import find_executable
    wrapper_path = find_executable('gitzebo-ssh-wrapper')

    # make sure ssh directory exists
    target_dir = os.path.join(home, '.ssh')
    try:
        os.makedirs(target_dir)
        os.chmod(target_dir, 0700)
    except OSError as e:
        if e.errno == errno.EEXIST and os.path.isdir(target_dir):
            pass
        else:
            raise

    # write resultset to file
    target_path = os.path.join(target_dir, 'authorized_keys')
    # TODO: eventually allow for DSA keys, too?
    venv = ''
    venv_dir = os.getenv('VIRTUAL_ENV', None)
    if venv_dir:
        venv = ' VIRTUAL_ENV=' + venv_dir
    with open(target_path, 'w') as f:
        for row in db.execute(s):
            f.write('command="{wrapper} USERNAME={username}{venv}",'
                    'no-port-forwarding,'
                    'no-X11-forwarding,'
                    'no-agent-forwarding,'
                    'no-pty'
                    ' ssh-rsa {key}\n'.format(
                        wrapper=wrapper_path,
                        username=row['user_name'],
                        key=row['public_key'],
                        venv=venv,  # virtualenv spec, if applicable
                    ))
Beispiel #13
0
def get_key_by_key(key):
    """
    Look up a key by the actual key (reverse lookup).

    Used to enforce uniqueness of keys, so that we don't generate an
    invalid authorized_keys file.
    """
    s = select([keys]).where(keys.c.public_key == key)
    result = db.execute(s)
    return result.fetchone()
Beispiel #14
0
def get_key_by_key(key):
    """
    Look up a key by the actual key (reverse lookup).

    Used to enforce uniqueness of keys, so that we don't generate an
    invalid authorized_keys file.
    """
    s = select([keys]).where(keys.c.public_key == key)
    result = db.execute(s)
    return result.fetchone()
Beispiel #15
0
def delete_key(key_id):
    # delete it!
    s = keys.delete().where(keys.c.key_id == key_id)
    result = db.execute(s)
    if result.rowcount != 1:
        raise Exception("Deleted {0} rows, expected to delete one.".format(
            result.rowcount))

    # insure the filesystem reflects changes and return
    regenerate_authorized_keys()
    return result.rowcount
Beispiel #16
0
def get_repos_for_user(user_id):
    columns = repos.c + [
        repo_acls.c.is_owner, repo_acls.c.can_read, repo_acls.c.can_write,
        repo_acls.c.can_rewind, repo_acls.c.can_create_tag,
        repo_acls.c.can_modify_tag
    ]
    s = select(columns).where(
        (repo_acls.c.repository_id == repos.c.repository_id)
        & (repo_acls.c.user_id == user_id)
        & (repo_acls.c.can_read | repo_acls.c.is_owner))
    return db.execute(s).fetchall()
Beispiel #17
0
def delete_key(key_id):
    # delete it!
    s = keys.delete().where(keys.c.key_id == key_id)
    result = db.execute(s)
    if result.rowcount != 1:
        raise Exception("Deleted {0} rows, expected to delete one.".format(
            result.rowcount))

    # insure the filesystem reflects changes and return
    regenerate_authorized_keys()
    return result.rowcount
Beispiel #18
0
def get_repos_for_user(user_id):
    columns = repos.c + [
        repo_acls.c.is_owner,
        repo_acls.c.can_read,
        repo_acls.c.can_write,
        repo_acls.c.can_rewind,
        repo_acls.c.can_create_tag,
        repo_acls.c.can_modify_tag,
    ]
    s = select(columns).where(
        (repo_acls.c.repository_id == repos.c.repository_id)
        & (repo_acls.c.user_id == user_id)
        & (repo_acls.c.can_read | repo_acls.c.is_owner)
    )
    return db.execute(s).fetchall()
Beispiel #19
0
def create_key(user_id, key, name):
    # validate data
    if not validate_key(key):
        raise ValueError("Invalid public key: '" + key + "'")

    # validate uniqueness
    if get_key_by_key(key):
        raise Exception("Key already exists in database.")

    # insert key
    stmt = keys.insert().values(user_id=user_id, name=name, public_key=key)
    result = db.execute(stmt)

    # insure the filesystem reflects changes and return
    regenerate_authorized_keys()
    return result.inserted_primary_key
Beispiel #20
0
def get_repo_acls(repo_id, user_id=None):
    """
    Get a list of the permissions each user has on this repo.

    If user_id is provided, the returned list will only contain that user's
    ACLs.
    """
    # branch if filtering by user ID
    user_clause = True
    if user_id:
        user_clause = users.c.user_id == user_id

    sq = select([repo_acls]).where(repo_acls.c.repository_id == repo_id).alias()
    columns = [c for c in users.c]
    columns += [c for c in sq.c if c not in [sq.c.user_id]]
    s = users.outerjoin(sq, (users.c.user_id == sq.c.user_id)).select(user_clause).with_only_columns(columns)
    result = db.execute(s)
    return result.fetchall()
Beispiel #21
0
def create_user(name, password, commit_name=None, commit_email=None, can_create_users=False, can_create_repositories=False):
    salt = generate_salt()
    hash = hash_password(password, salt)
    if commit_name is None:
        commit_name = name
    if commit_email is None:
        commit_email = ''
    stmt = users.insert().values(
        user_name=name,
        commit_name=commit_name,
        commit_email=commit_email,
        pass_salt=salt,
        pass_hash=hash,
        can_create_users=can_create_users,
        can_create_repositories=can_create_repositories,
    )
    result = db.execute(stmt)
    return result.inserted_primary_key[0]
Beispiel #22
0
def create_key(user_id, key, name):
    # validate data
    if not validate_key(key):
        raise ValueError("Invalid public key: '" + key + "'")

    # validate uniqueness
    if get_key_by_key(key):
        raise Exception("Key already exists in database.")

    # insert key
    stmt = keys.insert().values(
        user_id=user_id,
        name=name,
        public_key=key)
    result = db.execute(stmt)

    # insure the filesystem reflects changes and return
    regenerate_authorized_keys()
    return result.inserted_primary_key
Beispiel #23
0
def get_repo_acls(repo_id, user_id=None):
    """
    Get a list of the permissions each user has on this repo.

    If user_id is provided, the returned list will only contain that user's
    ACLs.
    """
    # branch if filtering by user ID
    user_clause = True
    if user_id:
        user_clause = users.c.user_id == user_id

    sq = (select([repo_acls
                  ]).where(repo_acls.c.repository_id == repo_id).alias())
    columns = [c for c in users.c]
    columns += [c for c in sq.c if c not in [sq.c.user_id]]
    s = (users.outerjoin(
        sq, (users.c.user_id
             == sq.c.user_id)).select(user_clause).with_only_columns(columns))
    result = db.execute(s)
    return result.fetchall()
Beispiel #24
0
def create_user(name,
                password,
                commit_name=None,
                commit_email=None,
                can_create_users=False,
                can_create_repositories=False):
    salt = generate_salt()
    hash = hash_password(password, salt)
    if commit_name is None:
        commit_name = name
    if commit_email is None:
        commit_email = ''
    stmt = users.insert().values(
        user_name=name,
        commit_name=commit_name,
        commit_email=commit_email,
        pass_salt=salt,
        pass_hash=hash,
        can_create_users=can_create_users,
        can_create_repositories=can_create_repositories,
    )
    result = db.execute(stmt)
    return result.inserted_primary_key[0]
Beispiel #25
0
def delete_user(user_id):
    s = repo_acls.delete().where(repo_acls.c.user_id == user_id)
    result = db.execute(s)
    s = users.delete().where(users.c.user_id == user_id)
    result = db.execute(s)
Beispiel #26
0
def get_key_by_id(key_id):
    s = select([keys]).where(keys.c.key_id == key_id)
    result = db.execute(s)
    return result.fetchone()
Beispiel #27
0
def get_keys(user_id):
    s = select([keys]).where(keys.c.user_id == user_id)
    result = db.execute(s)
    return result.fetchall()
Beispiel #28
0
def get_users():
    s = select([users])
    result = db.execute(s)
    return result.fetchall()
Beispiel #29
0
def get_key_by_id(key_id):
    s = select([keys]).where(keys.c.key_id == key_id)
    result = db.execute(s)
    return result.fetchone()
Beispiel #30
0
def get_repo_by_id(id):
    s = select([repos]).where(repos.c.repository_id == id)
    result = db.execute(s)
    return result.fetchone()
Beispiel #31
0
def get_user(name):
    s = select([users]).where(users.c.user_name == name)
    result = db.execute(s)
    return result.fetchone()
Beispiel #32
0
def get_user_by_id(id):
    s = select([users]).where(users.c.user_id == id)
    result = db.execute(s)
    return result.fetchone()
Beispiel #33
0
def get_repo(name):
    s = select([repos]).where(repos.c.repository_name == name)
    result = db.execute(s)
    return result.fetchone()
Beispiel #34
0
def get_users():
    s = select([users])
    result = db.execute(s)
    return result.fetchall()
Beispiel #35
0
def get_repo(name):
    s = select([repos]).where(repos.c.repository_name == name)
    result = db.execute(s)
    return result.fetchone()
Beispiel #36
0
def get_keys(user_id):
    s = select([keys]).where(keys.c.user_id == user_id)
    result = db.execute(s)
    return result.fetchall()
Beispiel #37
0
def get_user(name):
    s = select([users]).where(users.c.user_name == name)
    result = db.execute(s)
    return result.fetchone()
Beispiel #38
0
def delete_user(user_id):
    s = repo_acls.delete().where(repo_acls.c.user_id == user_id)
    result = db.execute(s)
    s = users.delete().where(users.c.user_id == user_id)
    result = db.execute(s)
Beispiel #39
0
def get_user_by_id(id):
    s = select([users]).where(users.c.user_id == id)
    result = db.execute(s)
    return result.fetchone()
Beispiel #40
0
def get_repo_by_id(id):
    s = select([repos]).where(repos.c.repository_id == id)
    result = db.execute(s)
    return result.fetchone()