Пример #1
0
 def get_github_client(self, user):
     assert user
     identity = Identity.query.filter(Identity.provider == 'github',
                                      Identity.user_id == user.id).first()
     if 'repo' not in identity.config['scopes']:
         raise IdentityNeedsUpgrade(
             scope='repo',
             identity=identity,
         )
     return GitHubClient(token=identity.config['access_token']), identity
Пример #2
0
def get_github_client(user: User, scopes=()) -> Tuple[GitHubClient, Identity]:
    identity = Identity.query.filter(Identity.provider == "github",
                                     Identity.user_id == user.id).first()

    if not identity:
        raise ApiUnauthorized

    for scope in scopes:
        if scope not in identity.scopes:
            raise IdentityNeedsUpgrade(scope=scope, identity=identity)

    return GitHubClient(token=identity.config["access_token"]), identity
Пример #3
0
    def get(self):
        redirect_uri = request.url
        flow = get_auth_flow(redirect_uri=redirect_uri)
        try:
            oauth_response = flow.step2_exchange(request.args['code'])
        except FlowExchangeError:
            return redirect('/?auth_error=true')

        scopes = oauth_response.token_response['scope'].split(',')

        if 'user:email' not in scopes:
            raise NotImplementedError

        # fetch user details
        github = GitHubClient(token=oauth_response.access_token)
        user_data = github.get('/user')

        identity_config = {
            'access_token': oauth_response.access_token,
            'refresh_token': oauth_response.refresh_token,
            'scopes': scopes,
            'login': user_data['login'],
        }

        email = user_data.get('email')
        # no primary/public email specified
        if not email:
            emails = github.get('/user/emails')
            email = next((
                e['email'] for e in emails
                if e['verified'] and e['primary']
            ))
        try:
            with db.session.begin_nested():
                user = User(
                    email=email,
                )
                db.session.add(user)
                identity = Identity(
                    user=user,
                    external_id=str(user_data['id']),
                    provider='github',
                    config=identity_config,
                )
                db.session.add(identity)
            user_id = user.id
        except IntegrityError:
            identity = Identity.query.filter(
                Identity.external_id == str(user_data['id']),
                Identity.provider == 'github',
            ).first()
            identity.config = identity_config
            db.session.add(identity)
            user_id = identity.user_id

        db.session.commit()

        # forcefully expire a session after permanent_session_lifetime
        # Note: this is enforced in zeus.auth
        auth.login_user(user_id)

        return redirect(url_for(self.complete_url))
Пример #4
0
    def get(self):
        # TODO(dcramer): handle errors
        oauth = get_oauth_session(state=session.pop("oauth_state", None))
        try:
            resp = oauth.fetch_token(
                GITHUB_TOKEN_URI,
                client_secret=current_app.config["GITHUB_CLIENT_SECRET"],
                authorization_response=request.url,
            )
        except OAuth2Error:
            current_app.logger.exception("oauth.error")
            # redirect, as this is likely temporary based on server data
            return redirect(auth.get_redirect_target(clear=True) or "/")

        if resp is None or resp.get("access_token") is None:
            return Response("Access denied: reason=%s error=%s resp=%s" %
                            (request.args["error"],
                             request.args["error_description"], resp))

        assert resp.get("token_type") == "bearer"

        scopes = resp["scope"][0].split(",")
        if "user:email" not in scopes:
            raise NotImplementedError

        # fetch user details
        client = GitHubClient(token=resp["access_token"])
        user_data = client.get("/user")

        identity_config = {
            "access_token": resp["access_token"],
            "refresh_token": resp.get("refresh_token"),
            "login": user_data["login"],
        }

        email_list = client.get("/user/emails")
        email_list.append({
            "email":
            "{}@users.noreply.github.com".format(user_data["login"]),
            "verified":
            True,
        })

        primary_email = user_data.get("email")
        # HACK(dcramer): capture github's anonymous email addresses when they're not listed
        # (we haven't actually confirmed they're not listed)
        if not primary_email:
            primary_email = next((e["email"] for e in email_list
                                  if e["verified"] and e["primary"]))

        try:
            # we first attempt to create a new user + identity
            with db.session.begin_nested():
                user = User(email=primary_email)
                db.session.add(user)
                identity = Identity(
                    user=user,
                    external_id=str(user_data["id"]),
                    provider="github",
                    scopes=scopes,
                    config=identity_config,
                )
                db.session.add(identity)
            user_id = user.id
            new_user = True
        except IntegrityError:
            # if that fails, assume the identity exists
            identity = Identity.query.filter(
                Identity.external_id == str(user_data["id"]),
                Identity.provider == "github",
            ).first()

            # and if it doesnt, attempt to find a matching user,
            # as it means the failure above was due to that
            if not identity:
                user = User.query.filter(User.email == primary_email).first()
                assert (
                    user
                )  # this should not be possible unless we've got a race condition
                identity = Identity(
                    user=user,
                    external_id=str(user_data["id"]),
                    provider="github",
                    scopes=scopes,
                    config=identity_config,
                )
                db.session.add(identity)
                user_id = user.id
            else:
                identity.config = identity_config
                identity.scopes = scopes
                db.session.add(identity)
                user_id = identity.user_id
            new_user = False
        db.session.flush()

        for email in email_list:
            try:
                with db.session.begin_nested():
                    db.session.add(
                        Email(
                            user_id=user_id,
                            email=email["email"],
                            verified=email["verified"],
                        ))
            except IntegrityError:
                pass

        db.session.commit()

        # forcefully expire a session after permanent_session_lifetime
        # Note: this is enforced in zeus.auth
        auth.login_user(user_id)

        user = auth.get_current_user()
        if new_user:
            # update synchronously so the new user has a better experience
            sync_github_access(user_id=user.id)
        else:
            sync_github_access.delay(user_id=user.id)

        next_uri = auth.get_redirect_target(clear=True) or "/"
        if "/login" in next_uri or "/auth/github" in next_uri:
            next_uri = "/"
        return redirect(next_uri)
Пример #5
0
    def get(self):
        redirect_uri = request.url
        flow = get_auth_flow(redirect_uri=redirect_uri)
        try:
            oauth_response = flow.step2_exchange(request.args['code'])
        except FlowExchangeError:
            return redirect('/?auth_error=true')

        scopes = oauth_response.token_response['scope'].split(',')

        if 'user:email' not in scopes:
            raise NotImplementedError

        # fetch user details
        github = GitHubClient(token=oauth_response.access_token)
        user_data = github.get('/user')

        identity_config = {
            'access_token': oauth_response.access_token,
            'refresh_token': oauth_response.refresh_token,
            'login': user_data['login'],
        }

        email_list = github.get('/user/emails')
        email_list.append({
            'email':
            '{}@users.noreply.github.com'.format(user_data['login']),
            'verified':
            True,
        })

        primary_email = user_data.get('email')
        # HACK(dcramer): capture github's anonymous email addresses when they're not listed
        # (we haven't actually confirmed they're not listed)
        if not primary_email:
            primary_email = next((e['email'] for e in email_list
                                  if e['verified'] and e['primary']))

        try:
            # we first attempt to create a new user + identity
            with db.session.begin_nested():
                user = User(email=primary_email, )
                db.session.add(user)
                identity = Identity(
                    user=user,
                    external_id=str(user_data['id']),
                    provider='github',
                    scopes=scopes,
                    config=identity_config,
                )
                db.session.add(identity)
            user_id = user.id
        except IntegrityError:
            # if that fails, assume the identity exists
            identity = Identity.query.filter(
                Identity.external_id == str(user_data['id']),
                Identity.provider == 'github',
            ).first()

            # and if it doesnt, attempt to find a matching user,
            # as it means the failure above was due to that
            if not identity:
                user = User.query.filter(User.email == primary_email).first()
                assert user  # this should not be possible unless we've got a race condition
                identity = Identity(
                    user=user,
                    external_id=str(user_data['id']),
                    provider='github',
                    scopes=scopes,
                    config=identity_config,
                )
                db.session.add(identity)
                user_id = user.id
            else:
                identity.config = identity_config
                identity.scopes = scopes
                db.session.add(identity)
                user_id = identity.user_id

        db.session.flush()

        for email in email_list:
            try:
                with db.session.begin_nested():
                    db.session.add(
                        Email(
                            user_id=user_id,
                            email=email['email'],
                            verified=email['verified'],
                        ))
            except IntegrityError:
                pass

        db.session.commit()

        # forcefully expire a session after permanent_session_lifetime
        # Note: this is enforced in zeus.auth
        auth.login_user(user_id)

        # now lets try to update the repos they have access to based on whats
        # enabled
        user = auth.get_current_user()
        grant_access_to_existing_repos(user)

        return redirect(auth.get_redirect_target(clear=True) or '/')
Пример #6
0
    def post(self, org: Organization):
        """
        Create a new repository.
        """
        provider = (request.get_json() or {}).get('provider', 'native')

        if provider == 'github':
            schema = github_repo_schema
        elif provider == 'native':
            schema = repo_schema
        else:
            raise NotImplementedError

        result = self.schema_from_request(schema, partial=True)
        if result.errors:
            return self.respond(result.errors, 403)
        data = result.data

        if provider == 'github':
            # get their credentials
            identity = Identity.query.filter(
                Identity.provider == 'github', Identity.user_id == auth.get_current_user().id
            ).first()
            if 'repo' not in identity.config['scopes']:
                return self.respond(
                    {
                        'needUpgrade': True,
                        'upgradeUrl': '/auth/github/upgrade'
                    }, 401
                )
            assert identity

            # fetch repository details using their credentials
            github = GitHubClient(token=identity.config['access_token'])
            repo_data = github.get('/repos/{}'.format(data['github_name']))

            repo, created = Repository.query.filter(
                Repository.provider == RepositoryProvider.github,
                Repository.external_id == str(repo_data['id']),
            ).first(), False
            if repo is None:
                # bind various github specific attributes
                repo, created = Repository(
                    organization=org,
                    backend=RepositoryBackend.git,
                    provider=RepositoryProvider.github,
                    status=RepositoryStatus.active,
                    external_id=str(repo_data['id']),
                    url=repo_data['clone_url'],
                    data={'github': {
                        'full_name': repo_data['full_name']
                    }},
                ), True
                db.session.add(repo)

                # generate a new private key for use on github
                key = ssh.generate_key()
                db.session.add(
                    ItemOption(
                        item_id=repo.id,
                        name='auth.private-key',
                        value=key.private_key,
                    )
                )

                # register key with github
                github.post(
                    '/repos/{}/keys'.format(repo.data['github']['full_name']),
                    json={
                        'title': 'zeus',
                        'key': key.public_key,
                        'read_only': True,
                    }
                )
        elif provider == 'native':
            repo, created = Repository(
                organization=org,
                status=RepositoryStatus.active,
                **data,
            ), True
            db.session.add(repo)

        db.session.flush()

        try:
            with db.session.begin_nested():
                db.session.add(
                    RepositoryAccess(
                        organization=org,
                        repository=repo,
                        user=auth.get_current_user(),
                    )
                )
                db.session.flush()
        except IntegrityError:
            raise
            pass

        db.session.commit()

        if created:
            import_repo.delay(repo_id=repo.id)

        return self.respond_with_schema(repo_schema, repo)
Пример #7
0
 def get_github_client(self, user):
     assert user
     identity = Identity.query.filter(Identity.provider == 'github',
                                      Identity.user_id == user.id).first()
     return GitHubClient(token=identity.config['access_token']), identity