~edwargix/git.sr.ht

8c828abeb29a81c9f3406d7d1a8e6a9a5b1d7a80 — Drew DeVault 7 years ago 411c109
Fetch user SSH keys from meta.sr.ht
A gitsrht/alembic/versions/4b8847962942_add_ssh_key_table.py => gitsrht/alembic/versions/4b8847962942_add_ssh_key_table.py +27 -0
@@ 0,0 1,27 @@
"""Add ssh key table

Revision ID: 4b8847962942
Revises: 27ad57b7c4a5
Create Date: 2019-02-14 12:04:24.026615

"""

# revision identifiers, used by Alembic.
revision = '4b8847962942'
down_revision = '27ad57b7c4a5'

from alembic import op
import sqlalchemy as sa


def upgrade():
    op.create_table('sshkey',
        sa.Column("id", sa.Integer, primary_key=True),
        sa.Column("user_id", sa.Integer, sa.ForeignKey('user.id'), nullable=False),
        sa.Column("meta_id", sa.Integer, nullable=False, unique=True, index=True),
        sa.Column("key", sa.String(4096), nullable=False),
        sa.Column("fingerprint", sa.String(512), nullable=False))


def downgrade():
    op.drop_table('sshkey')

M gitsrht/app.py => gitsrht/app.py +2 -2
@@ 6,7 6,7 @@ from functools import lru_cache
from gitsrht import urls
from gitsrht.git import commit_time, trim_commit
from gitsrht.repos import GitRepoApi
from gitsrht.service import GitOAuthService, webhooks_notify
from gitsrht.service import oauth_service, webhooks_notify
from gitsrht.types import Access, Redirect, Repository, User
from scmsrht.flask import ScmSrhtFlask
from srht.config import cfg


@@ 21,7 21,7 @@ class GitApp(ScmSrhtFlask):
                access_class=Access, redirect_class=Redirect,
                repository_class=Repository, user_class=User,
                repo_api=GitRepoApi(),
                oauth_service=GitOAuthService())
                oauth_service=oauth_service)

        from gitsrht.blueprints.repo import repo
        from gitsrht.blueprints.stats import stats

M gitsrht/service.py => gitsrht/service.py +47 -4
@@ 1,6 1,7 @@
from flask import Blueprint, request, url_for
from gitsrht.types import User, OAuthToken
from gitsrht.types import User, OAuthToken, SSHKey
from srht.api import get_results
from srht.database import db
from srht.config import cfg
from srht.flask import csrf_bypass
from srht.oauth import AbstractOAuthService


@@ 8,6 9,7 @@ import json
import requests

origin = cfg("git.sr.ht", "origin")
meta_origin = cfg("meta.sr.ht", "origin")
client_id = cfg("git.sr.ht", "oauth-client-id")
client_secret = cfg("git.sr.ht", "oauth-client-secret")
builds_client_id = cfg("builds.sr.ht", "oauth-client-id", default=None)


@@ 20,6 22,25 @@ class GitOAuthService(AbstractOAuthService):
                ] if builds_client_id else []),
                token_class=OAuthToken, user_class=User)

    def ensure_user_sshkey(self, user, meta_key):
        """
        Ensures this SSH key is registered with this user, and returns True if
        their authorized_keys file needs to be regenerated.

        `meta_key` should be the key object returned from meta.sr.ht.
        """
        key = SSHKey.query.filter(
                SSHKey.meta_id == meta_key["id"]).one_or_none()
        if key:
            return False
        key = SSHKey()
        key.user_id = user.id
        key.meta_id = meta_key["id"]
        key.key = meta_key["key"]
        key.fingerprint = meta_key["fingerprint"]
        db.session.add(key)
        return True

    def ensure_meta_webhooks(self, user, webhooks):
        webhook_url = origin + url_for("webhooks.notify.notify_keys")
        webhooks.update({


@@ 27,6 48,16 @@ class GitOAuthService(AbstractOAuthService):
        })
        super().ensure_meta_webhooks(user, webhooks)

    def lookup_or_register(self, token, token_expires, scopes):
        user = super().lookup_or_register(token, token_expires, scopes)
        keys_url = f"{meta_origin}/api/user/ssh-keys"
        for key in get_results(keys_url, user.oauth_token):
            self.ensure_user_sshkey(user, key)
        db.session.commit()
        return user

oauth_service = GitOAuthService()

webhooks_notify = Blueprint("webhooks.notify", __name__)

@csrf_bypass


@@ 34,6 65,18 @@ webhooks_notify = Blueprint("webhooks.notify", __name__)
def notify_keys():
    payload = json.loads(request.data.decode('utf-8'))
    event = request.headers.get("X-Webhook-Event")
    # TODO: Store these keys in the database
    print(event, payload)
    return "Thanks!"
    # TODO: Regenerate authorized_keys
    if event == "ssh-key:add":
        user = User.query.filter(
                User.username == payload["owner"]["name"]).one_or_none()
        oauth_service.ensure_user_sshkey(user, payload)
        db.session.commit()
        return "Added user's SSH key, thanks!"
    elif event == "ssh-key:remove":
        key = SSHKey.query.filter(
                SSHKey.meta_id == payload["id"]).one_or_none()
        if key:
            db.session.delete(key)
            db.session.commit()
        return "Removed user's SSH key, thanks!"
    return f"Unexpected event {event}"

M gitsrht/types/__init__.py => gitsrht/types/__init__.py +2 -0
@@ 17,3 17,5 @@ class Redirect(Base, BaseRedirectMixin):

class Repository(Base, BaseRepositoryMixin):
    pass

from gitsrht.types.sshkey import SSHKey

A gitsrht/types/sshkey.py => gitsrht/types/sshkey.py +15 -0
@@ 0,0 1,15 @@
import sqlalchemy as sa
import sqlalchemy_utils as sau
from srht.database import Base

class SSHKey(Base):
    __tablename__ = 'sshkey'
    id = sa.Column(sa.Integer, primary_key=True)
    user_id = sa.Column(sa.Integer, sa.ForeignKey('user.id'), nullable=False)
    user = sa.orm.relationship('User', backref=sa.orm.backref('ssh_keys'))
    meta_id = sa.Column(sa.Integer, nullable=False, unique=True, index=True)
    key = sa.Column(sa.String(4096), nullable=False)
    fingerprint = sa.Column(sa.String(512), nullable=False)

    def __repr__(self):
        return '<SSHKey {} {}>'.format(self.id, self.fingerprint)