コード例 #1
0
 def oauth():
     if not request.args["code"]:
         return jsonify({"Error": "sadcat"}), 500
     resp = requests.post(
         "https://slack.com/api/oauth.v2.access",
         {
             "code": request.args["code"],
             "client_id": get_secret(secret_name="CLIENT_ID"),
             "client_secret": get_secret(secret_name="CLIENT_SECRET"),
         },
     )
     if resp.status_code == 200:
         data = resp.json()
         bot_token = data["access_token"]
         workspace_data = requests.post(
             "https://slack.com/api/auth.test",
             headers={"Authorization": "Bearer {}".format(bot_token)},
         ).json()
         workspace_url = workspace_data["url"]
         workspace = re.match(
             r"https://([a-zA-Z\-0-9]+)\.slack\.com", workspace_url
         ).group(1)
         store_bot_token(get_course(workspace), data["team"]["id"], bot_token)
         store_user_token(
             data["authed_user"]["id"], data["authed_user"]["access_token"]
         )
         with connect_db() as db:
             db(
                 "DELETE FROM silenced_users WHERE user = (%s)",
                 [data["authed_user"]["id"]],
             )
         return redirect(workspace_url)
     return jsonify({"Error": "sadcat"}), 500
コード例 #2
0
def gen_env_variables(app: App, pr_number: int):
    if app.config["deploy_type"] == "hosted":
        database_url = sqlalchemy.engine.url.URL(
            drivername="mysql",
            host=DB_IP_ADDRESS,
            username="******",
            password=get_secret(secret_name="DATABASE_PW"),
            database=app.name.replace("-", "_"),
        ).__to_string__(hide_password=False)
    elif app.config["deploy_type"] in CLOUD_RUN_DEPLOY_TYPES:
        database_url = sqlalchemy.engine.url.URL(
            drivername="mysql+pymysql",
            username="******",
            password=get_secret(secret_name="DATABASE_PW"),
            database=app.name.replace("-", "_"),
            query={
                "unix_socket": "{}/{}".format("/cloudsql", DB_INSTANCE_NAME)
            },
        ).__to_string__(hide_password=False)
    else:
        database_url = None

    return dict(
        ENV="prod",
        DATABASE_URL=database_url,
        INSTANCE_CONNECTION_NAME=DB_INSTANCE_NAME,
        APP_MASTER_SECRET=gen_master_secret(app, pr_number),
        **(load_all_secrets(
            created_app_name=app.name) if pr_number == 0 else {}),
    )
コード例 #3
0
def webhook():
    if not hmac.compare_digest(
        "sha1="
        + hmac.new(
            get_secret(secret_name="GITHUB_WEBHOOK_SECRET").encode("ascii"),
            request.get_data(),
            "sha1",
        ).hexdigest(),
        request.headers["X-Hub-Signature"],
    ):
        abort(401)

    payload = request.json

    g = Github(get_secret(secret_name="GITHUB_ACCESS_TOKEN"))

    if "pusher" in payload and payload["ref"] == "refs/heads/master":
        base_repo = g.get_repo(GITHUB_REPO)
        repo = g.get_repo(payload["repository"]["id"])
        sha = payload["after"]
        land_commit(
            sha,
            repo,
            base_repo,
            None,
            [
                file
                for commit in payload["commits"]
                for file in commit["added"] + commit["modified"] + commit["removed"]
            ],
        )
        delete_unused_services()

    if "pull_request" in payload:
        repo_id = payload["repository"]["id"]
        repo = g.get_repo(repo_id)
        pr = repo.get_pull(payload["pull_request"]["number"])

        if payload["action"] in ("opened", "synchronize", "reopened"):
            if repo.full_name != GITHUB_REPO:
                land_commit(pr.head.sha, repo, g.get_repo(GITHUB_REPO), pr, [])
            else:
                for target in determine_targets(repo, pr.get_files()):
                    report_build_status(
                        target,
                        pr.number,
                        pack(repo.clone_url, pr.head.sha),
                        BuildStatus.pushed,
                        None,
                        None,
                        private=True,
                    )

        elif payload["action"] == "closed":
            set_pr_comment("PR closed, shutting down PR builds...", pr)
            delete_unused_services(pr.number)
            set_pr_comment("All PR builds shut down.", pr)

    return ""
コード例 #4
0
ファイル: main.py プロジェクト: 61a-ide/cs61a-apps
def add_domain(app, is_staging, course, domain):
    try:
        if app != "auth":
            abort(401)

        app = APP_LOOKUP[domain.split(".")[0]]

        with connect_db() as db:
            status = db("SELECT status FROM hosted_apps WHERE domain=(%s)",
                        [domain]).fetchone()
            if status is not None and status[0] == Status.SUCCESS:
                return ""  # domain already provisioned
            db("DELETE FROM hosted_apps WHERE domain=(%s)", [domain])

        with connect_db() as db:
            db(
                "INSERT INTO hosted_apps (domain, course, app, status) VALUES (%s, %s, %s, %s)",
                [domain, course, app, Status.VALIDATING.value],
            )

        try:
            ip = socket.gethostbyname(domain)
        except socket.gaierror:
            ip = None
        if ip != socket.gethostbyname("proxy.cs61a.org"):
            set_status(domain, Status.DNS_INVALID)
            return

        set_status(domain, Status.PROVISIONING)

        try:
            requests.post(
                "https://proxy.cs61a.org/create_domain",
                json=dict(
                    app=app,
                    domain=domain,
                    target=get_base_hostname(target_app=app),
                    secret=get_secret(secret_name="DOMAIN_WEBHOOK_SECRET"),
                ),
            ).raise_for_status()
        except requests.exceptions.ConnectionError:
            pass  # nginx restarts so the connection crashes

        sleep(5)

        if not requests.get(f"https://{domain}/").ok:
            set_status(domain, Status.PROVISIONING_FAILED)
            return

        set_status(domain, Status.UPDATING_OAUTH)
        # TODO
        set_status(domain, Status.SUCCESS)
        return
    except:
        set_status(domain, Status.INTERNAL_ERROR)
        raise
コード例 #5
0
def handle_deploy_prod_app_sync(app, is_staging, target_app):
    if app != "buildserver" or is_staging:
        abort(401)
    g = Github(get_secret(secret_name="GITHUB_ACCESS_TOKEN"))
    repo = g.get_repo(GITHUB_REPO)
    land_commit(
        repo.get_branch(repo.default_branch).commit.sha,
        repo,
        repo,
        None,
        [f"{target_app}/main.py"],
    )
コード例 #6
0
def handle_trigger_build_sync(app, is_staging, pr_number, target_app=None):
    if app not in ("slack", "buildserver") or is_staging:
        raise PermissionError

    g = Github(get_secret(secret_name="GITHUB_ACCESS_TOKEN"))
    repo = g.get_repo(GITHUB_REPO)
    pr = repo.get_pull(pr_number)

    if DO_NOT_BUILD in [l.name for l in pr.labels]:
        raise PermissionError

    land_commit(pr.head.sha, repo, repo, pr, pr.get_files(), target_app=target_app)
コード例 #7
0
def clear_queue(repo: str, pr_number: int):
    g = Github(get_secret(secret_name="GITHUB_ACCESS_TOKEN"))
    repo = g.get_repo(repo)
    pr = repo.get_pull(pr_number) if pr_number else None
    land_commit(
        pr.head.sha if pr else repo.get_branch(repo.default_branch).commit.sha,
        repo,
        g.get_repo(GITHUB_REPO),
        pr,
        [],
        dequeue_only=True,
    )
コード例 #8
0
ファイル: gs_export.py プロジェクト: 61a-ide/cs61a-apps
def export(name, gs_code):
    email = "*****@*****.**".strip()
    password = get_secret(secret_name="GRADESCOPE_PW").strip()

    print("Logging in...")

    gsapi = GradescopeAPIClient()
    if gsapi.log_in(email, password):
        gs = GradescopeClient()
        if gs.log_in(email, password):
            print("Logged in.\n")
        else:
            print("Frontend login failed :(", file=sys.stderr)
            sys.exit(1)
    else:
        print("Backend login failed :(", file=sys.stderr)
        sys.exit(1)

    print(f"Looking up {name}...")
    full_name = gs.get_assignment_name(COURSE_CODE, gs_code)

    if not full_name:
        print(f"Assignment for '{name}' not found :(", file=sys.stderr)
        sys.exit(1)

    print(f"Assignment '{full_name}' found. Downloading scores...")
    res = gs.download_scores(COURSE_CODE, gs_code)

    if res:
        p = Path(".").expanduser().absolute()
        if not p.exists():
            p.parent.mkdir(parents=True, exist_ok=True)
        if not p.is_file():
            p = p.joinpath(f"data/{name}.csv")

        with open(p, "wb+") as f:
            f.write(res)
        print("Done.\n")
    else:
        print(f"Download for '{full_name}' failed :(", file=sys.stderr)
        sys.exit(1)

    print("Converting to Okpy upload file...")
    gs_csv = pd.read_csv(f"data/{name}.csv")
    ok_csv = gs_csv[["SID", "Email", "Total Score"]]
    ok_csv["SID"] = ok_csv["SID"].fillna(0).astype(int).astype(str)

    ok_csv.to_csv(f"data/{name}.csv", index=False)
    print("Done.")

    return full_name
コード例 #9
0
ファイル: deploy.py プロジェクト: Cal-CS-61A-Staff/cs61a-apps
def run_pypi_deploy(app: App, pr_number: int):
    sh("python", "-m", "venv", "env")
    update_setup_py(app, pr_number)
    sh("env/bin/pip", "install", "setuptools")
    sh("env/bin/pip", "install", "wheel")
    sh("env/bin/python", "setup.py", "sdist", "bdist_wheel")
    sh(
        "twine",
        "upload",
        *(f"dist/{file}" for file in os.listdir("dist")),
        env=dict(
            TWINE_USERNAME="******",
            TWINE_PASSWORD=get_secret(secret_name="PYPI_PASSWORD"),
        ),
    )
コード例 #10
0
ファイル: security.py プロジェクト: 61a-ide/cs61a-apps
 def wrapped(*args, **kwargs):
     data = request.get_data().decode("utf-8")
     timestamp = request.headers["X-Slack-Request-Timestamp"]
     slack_signature = request.headers["X-Slack-Signature"]
     if abs(time.time() - int(timestamp)) > 60 * 5:
         abort(403)
     basestring = "v0:" + timestamp + ":" + data
     my_signature = ("v0=" + hmac.new(
         get_secret(secret_name="SIGNING_SECRET").encode(),
         basestring.encode(),
         hashlib.sha256,
     ).hexdigest())
     if hmac.compare_digest(my_signature.encode(),
                            slack_signature.encode()):
         return route(*args, **kwargs)
     else:
         abort(403)
コード例 #11
0
def load_dependencies(app: App, sha: str, repo: Repository):
    g = Github(get_secret(secret_name="GITHUB_ACCESS_TOKEN"))

    def clone_repo(repo_str: str):
        cloned_repo = g.get_repo(repo_str)
        cloned_sha = (sha if cloned_repo.full_name == repo.full_name else
                      cloned_repo.get_branch(
                          cloned_repo.default_branch).commit.sha)
        clone_commit(cloned_repo.clone_url, cloned_sha, in_place=True)

    with tmp_directory():
        for dependency in app.config["dependencies"]:
            folder_name = dependency["repo"].replace("/", "-")
            if not isdir(folder_name):
                # dependency is not already loaded
                mkdir(folder_name)
                chdir(folder_name)
                clone_repo(dependency["repo"])
                chdir("..")
            try:
                copytree(
                    os.path.join(folder_name, dependency["src"]),
                    os.path.join(app.name, dependency["dest"]),
                    symlinks=False,
                    dirs_exist_ok=True,
                )
            except NotADirectoryError:
                copyfile(
                    os.path.join(folder_name, dependency["src"]),
                    os.path.join(app.name, dependency["dest"]),
                )
        if app.config["repo"]:
            working_dir = gen_working_dir(app)
            mkdir(working_dir)
            chdir(working_dir)
            clone_repo(app.config["repo"])
            chdir("..")
コード例 #12
0
def trigger_build():
    if not is_staff("cs61a"):
        return login()
    email = get_user()["email"]
    if not is_admin(course="cs61a", email=email):
        abort(401)
    if "app" in request.args:
        target = request.args["app"]
    else:
        target = None

    pr_number = int(request.args["pr_number"])

    g = Github(get_secret(secret_name="GITHUB_ACCESS_TOKEN"))
    repo = g.get_repo(GITHUB_REPO)
    pr = repo.get_pull(pr_number)

    if DO_NOT_BUILD in [l.name for l in pr.labels]:
        return html(
            f"PR <code>{pr_number}</code> has a DO NOT BUILD label on it, so it cannot be built. Remove this label to build the PR."
        )

    trigger_build_sync(pr_number=pr_number, target_app=target, noreply=True)
    return html(f"Building PR <code>{pr_number}</code>!")
コード例 #13
0
def create_oauth_client(app):
    oauth = OAuth(app)

    if os.getenv("ENV") == "prod":
        consumer_key = CONSUMER_KEY
        app.secret_key = get_secret(secret_name="OKPY_OAUTH_SECRET")
    else:
        consumer_key = "local-dev-all"
        app.secret_key = "kmSPJYPzKJglOOOmr7q0irMfBVMRFXN"

    if not app.debug:
        app.config.update(
            SESSION_COOKIE_SECURE=True,
            SESSION_COOKIE_HTTPONLY=True,
            SESSION_COOKIE_SAMESITE="Lax",
        )

    remote = oauth.remote_app(
        "ok-server",  # Server Name
        consumer_key=consumer_key,
        consumer_secret=app.secret_key,
        request_token_params={
            "scope": "all",
            "state": lambda: security.gen_salt(10)
        },
        base_url="https://okpy.org/api/v3/",
        request_token_url=None,
        access_token_method="POST",
        access_token_url="https://okpy.org/oauth/token",
        authorize_url="https://okpy.org/oauth/authorize",
    )

    def kill_popup():
        response = app.make_response("<script> window.close(); </script>")
        response.delete_cookie(COOKIE_IS_POPUP)
        return response

    def check_req(uri, headers, body):
        """ Add access_token to the URL Request. """
        if "access_token" not in uri and session.get("dev_token"):
            params = {"access_token": session.get("dev_token")[0]}
            url_parts = list(urllib.parse.urlparse(uri))
            query = dict(urllib.parse.parse_qsl(url_parts[4]))
            query.update(params)

            url_parts[4] = urllib.parse.urlencode(query)
            uri = urllib.parse.urlunparse(url_parts)
        return uri, headers, body

    remote.pre_request = check_req

    @app.route("/oauth/popup_login")
    def popup_login():
        response = remote.authorize(
            callback=url_for("authorized", _external=True))
        response.set_cookie(COOKIE_IS_POPUP, value="")
        return response

    @app.route("/oauth/popup_logout")
    def popup_logout():
        session.pop("dev_token", None)
        return kill_popup()

    @app.route("/oauth/login")
    def login():
        response = remote.authorize(
            callback=url_for("authorized", _external=True))
        response.delete_cookie(COOKIE_IS_POPUP)
        return response

    @app.route("/oauth/authorized")
    def authorized():
        resp = remote.authorized_response()
        if resp is None:
            return "Access denied: error=%s" % (request.args["error"])
        if isinstance(resp, dict) and "access_token" in resp:
            session["dev_token"] = (resp["access_token"], "")

        if COOKIE_IS_POPUP in request.cookies:
            return kill_popup()
        if COOKIE_SHORTLINK_REDIRECT in request.cookies:
            return app.load_file(request.cookies[COOKIE_SHORTLINK_REDIRECT])
        else:
            return redirect("/")

    @app.route("/api/user", methods=["POST"])
    def client_method():
        if "dev_token" not in session:
            abort(401)
        token = session["dev_token"][0]
        r = requests.get(
            "https://okpy.org/api/v3/user/?access_token={}".format(token))
        if not r.ok:
            abort(401)
        return jsonify(r.json())

    @remote.tokengetter
    def get_oauth_token():
        return session.get("dev_token")

    app.remote = remote
コード例 #14
0
ファイル: oauth_client.py プロジェクト: 61a-ide/cs61a-apps
def create_oauth_client(
    app: flask.Flask,
    consumer_key,
    secret_key=None,
    success_callback=None,
    return_response=None,
):
    oauth = OAuth(app)

    if os.getenv("ENV") == "prod":
        if secret_key is None:
            app.secret_key = get_secret(secret_name="OKPY_OAUTH_SECRET")
        else:
            app.secret_key = secret_key
    else:
        consumer_key = "local-dev-all"
        app.secret_key = "kmSPJYPzKJglOOOmr7q0irMfBVMRFXN"

    if not app.debug:
        app.config.update(
            SESSION_COOKIE_SECURE=True,
            SESSION_COOKIE_HTTPONLY=True,
            SESSION_COOKIE_SAMESITE="Lax",
        )

    remote = oauth.remote_app(
        "ok-server",  # Server Name
        consumer_key=consumer_key,
        consumer_secret=app.secret_key,
        request_token_params={
            "scope": "all",
            "state": lambda: security.gen_salt(10)
        },
        base_url="https://okpy.org/api/v3/",
        request_token_url=None,
        access_token_method="POST",
        access_token_url="https://okpy.org/oauth/token",
        authorize_url="https://okpy.org/oauth/authorize",
    )

    def check_req(uri, headers, body):
        """ Add access_token to the URL Request. """
        if "access_token" not in uri and session.get("access_token"):
            params = {"access_token": session.get("access_token")[0]}
            url_parts = list(urllib.parse.urlparse(uri))
            query = dict(urllib.parse.parse_qsl(url_parts[4]))
            query.update(params)

            url_parts[4] = urllib.parse.urlencode(query)
            uri = urllib.parse.urlunparse(url_parts)
        return uri, headers, body

    remote.pre_request = check_req

    @app.route("/oauth/login")
    def login():
        if app.debug:
            response = remote.authorize(
                callback=url_for("authorized", _external=True))
        else:
            response = remote.authorize(
                url_for("authorized", _external=True, _scheme="https"))
        return response

    @app.route("/oauth/authorized")
    def authorized():
        resp = remote.authorized_response()
        if resp is None:
            return "Access denied: error=%s" % (request.args["error"])
        if isinstance(resp, dict) and "access_token" in resp:
            session["access_token"] = (resp["access_token"], "")
            if return_response:
                return_response(resp)

        if success_callback:
            success_callback()

        target = session.get(REDIRECT_KEY)
        if target:
            session.pop(REDIRECT_KEY)
            return redirect(target)
        return redirect(url_for("index"))

    @app.route("/api/user", methods=["POST"])
    def client_method():
        if "access_token" not in session:
            abort(401)
        token = session["access_token"][0]
        r = requests.get(
            "https://okpy.org/api/v3/user/?access_token={}".format(token))
        if not r.ok:
            abort(401)
        return jsonify(r.json())

    @remote.tokengetter
    def get_oauth_token():
        return session.get("access_token")

    app.remote = remote
コード例 #15
0
import json

from flask import request, url_for, abort
from werkzeug.utils import redirect

from common.rpc.auth import get_endpoint, list_courses, slack_workspace_name
from common.rpc.secrets import get_secret
from common.db import connect_db
from security import logged_in, get_staff_endpoints

REJECTED = object()
UNABLE = object()

CLIENT_ID = get_secret(secret_name="CLIENT_ID")


def get_add_to_slack_link(domain):
    return f"https://{domain}.slack.com/oauth/v2/authorize?client_id={CLIENT_ID}&scope=channels:join,channels:read,chat:write,users:read,users:read.email,groups:read&user_scope=channels:history,chat:write,groups:history,im:history,mpim:history,users:read"


with open("config.json") as f:
    CONFIG = json.load(f)


def init_db():
    with connect_db() as db:
        db("""CREATE TABLE IF NOT EXISTS tokens (
        user varchar(128),
        token text,
        PRIMARY KEY (`user`)
    );""")
コード例 #16
0
# this is used to trigger the worker via Cloud Build
import sys

from github import Github

from app_config import App
from build import clone_commit
from common.rpc.secrets import get_secret
from conf import GITHUB_REPO
from worker import land_app_worker

if __name__ == "__main__":
    g = Github(get_secret(secret_name="GITHUB_ACCESS_TOKEN"))
    _, app_name, pr_number, sha, repo_id = sys.argv
    base_repo = g.get_repo(GITHUB_REPO)
    clone_commit(
        base_repo.clone_url,
        sha if repo_id == base_repo.full_name else base_repo.get_branch(
            base_repo.default_branch).commit.sha,
    )
    land_app_worker(App(app_name), int(pr_number), sha, g.get_repo(repo_id))
コード例 #17
0
ファイル: auth.py プロジェクト: cmcmahon226/cs61a-apps
 def wrapped(**kwargs):
     secret = get_secret(secret_name="AUTH_SECRET")
     return func(**kwargs, secret=secret)
コード例 #18
0
from common.db import database_url
from common.rpc.secrets import get_secret

basedir = os.path.abspath(os.path.dirname(__file__))

ENV = os.getenv("ENV", "dev")

if ENV == "DEV_ON_PROD":
    ENV = "dev"

if ENV == "dev":
    DEBUG = True
    SECRET_KEY = "dev"
else:
    DEBUG = False
    SECRET_KEY = get_secret(secret_name="SESSION_COOKIE_SECRET")

SQLALCHEMY_DATABASE_URI = database_url

SQLALCHEMY_TRACK_MODIFICATIONS = False
DATABASE_CONNECT_OPTIONS = {}

LOCAL_TIMEZONE = os.getenv("TIMEZONE", "US/Pacific")

AUTH_KEY = "oh-queue-staging" if os.getenv("IN_STAGING") else "oh-queue"

if ENV == "dev":
    AUTH_SECRET = ""
    OK_KEY = "local-dev-email"
    OK_SECRET = "KH0mvknMUWT5w3U7zvz6wsUQZoy6UmQ"
else:
コード例 #19
0
def update_status(packed_ref: str, pr_number: int):
    g = Github(get_secret(secret_name="GITHUB_ACCESS_TOKEN"))
    repo_url, sha = unpack(packed_ref)
    repo_name = urlparse(repo_url).path.split(".")[0][
        1:
    ]  # This is awful ... but it works
    repo = g.get_repo(repo_name)

    # First we will update the commit-specific status indicator
    with connect_db() as db:
        statuses = db(
            "SELECT app, status FROM builds WHERE packed_ref=%s", [packed_ref]
        ).fetchall()
    statuses = [(app, BuildStatus(status)) for app, status in statuses]
    if all(status == BuildStatus.success for _, status in statuses):
        repo.get_commit(sha).create_status(
            "success",
            "https://logs.cs61a.org/service/buildserver",
            "All modified services built!",
            "Pusher",
        )
    elif any(status == BuildStatus.failure for _, status in statuses):
        repo.get_commit(sha).create_status(
            "failure",
            "https://logs.cs61a.org/service/buildserver",
            "Pusher failed to build a modified service",
            "Pusher",
        )
    elif all(
        status in (BuildStatus.building, BuildStatus.queued) for _, status in statuses
    ):
        repo.get_commit(sha).create_status(
            "pending",
            "https://logs.cs61a.org/service/buildserver",
            "Pusher is building all modified services",
            "Pusher",
        )
    else:
        # There are no failures, but not everything is building / built
        repo.get_commit(sha).create_status(
            "pending",
            "https://logs.cs61a.org/service/buildserver",
            "You must build all modified apps before merging",
            "Pusher",
        )

    if pr_number == 0:
        return

    pr = repo.get_pull(pr_number)
    # Now we will update the PR comment, looking at builds for all packed_refs in the PR
    apps = determine_targets(repo, pr.get_files())
    success = []
    failure = []
    running = []
    queued = []
    triggerable = []
    with connect_db() as db:
        for app in apps:
            successful_build = db(
                "SELECT url, log_url, unix, packed_ref FROM builds WHERE app=%s AND pr_number=%s AND status='success' ORDER BY unix DESC LIMIT 1",
                [app, pr_number],
            ).fetchone()
            if successful_build:
                url, log_url, success_unix, packed_ref = successful_build
                _, sha = unpack(packed_ref)
                if url:
                    for link in url.split(","):
                        success.append((app, link, sha, log_url))
                else:
                    success.append((app, None, sha, log_url))

            failed_build = db(
                "SELECT unix, log_url, packed_ref FROM builds WHERE app=%s AND pr_number=%s AND status='failure' ORDER BY unix DESC LIMIT 1",
                [app, pr_number],
            ).fetchone()
            if failed_build:
                unix, log_url, packed_ref = failed_build
                if not successful_build or success_unix < unix:
                    _, sha = unpack(packed_ref)
                    failure.append((app, sha, log_url))

            running_build = db(
                "SELECT packed_ref FROM builds WHERE app=%s AND pr_number=%s AND status='building'",
                [app, pr_number],
            ).fetchone()
            if running_build:
                [packed_ref] = running_build
                _, sha = unpack(packed_ref)
                running.append((app, sha))

            queued_build = db(
                "SELECT packed_ref FROM builds WHERE app=%s AND pr_number=%s AND status='queued'",
                [app, pr_number],
            ).fetchone()
            if queued_build:
                [packed_ref] = queued_build
                _, sha = unpack(packed_ref)
                queued.append((app, sha))

            latest_commit_build = db(
                "SELECT * FROM builds WHERE app=%s AND pr_number=%s AND packed_ref=%s AND status!='pushed'",
                [app, pr_number, pack(repo_url, pr.head.sha)],
            ).fetchone()
            if not latest_commit_build:
                triggerable.append(app)

    if repo.name == "berkeley-cs61a":
        message = f"## Build Status ([pr/{pr_number}]({pr.html_url}))\n\n"
    elif repo.name == "cs61a-apps":
        message = f"## Build Status ([apps/{pr_number}]({pr.html_url}))\n\n"
    else:
        message = f"## Build Status (#{pr_number})\n\n"

    if success:
        message += (
            "**Successful Builds**\n"
            + "\n".join(
                f" - [{host}](https://{host}) ({sha}) [[logs]({log_url})]"
                if host
                else f" - `{app}` ({sha}) [[logs]({log_url})]"
                for app, host, sha, log_url in success
            )
            + "\n\n"
        )

    if failure:
        message += (
            "**Failed Builds**\n"
            + "\n".join(
                f" - `{app}` ({sha}) [[logs]({log_url})]"
                for app, sha, log_url in failure
            )
            + "\n\n"
        )

    if running:
        message += (
            "**Running Builds**\n"
            + "\n".join(f" - `{app}` ({sha})" for app, sha in running)
            + "\n\n"
        )

    if queued:
        message += (
            "**Queued Builds**\n"
            + "\n".join(f" - `{app}` ({sha})" for app, sha in queued)
            + "\n\n"
        )

    if (success or failure or running or queued) and triggerable:
        message += "-----\n"

    if triggerable:
        message += (
            f"**[Click here]({url_for('trigger_build', pr_number=pr.number)})** to trigger all builds "
            f"for the most recent commit ({pr.head.sha})\n\n"
            "Or trigger builds individually:\n"
        ) + "\n".join(
            f" - [Click here]({url_for('trigger_build', pr_number=pr.number, app=app)}) "
            f"to build `{app}` at the most recent commit ({pr.head.sha})"
            for app in triggerable
        )

    set_pr_comment(message, pr)
コード例 #20
0
ファイル: db.py プロジェクト: 61a-ide/cs61a-apps
            "Are you sure? You are about to use the PROD database on a DEV build (y/N) "
        )
        .strip()
        .lower()
        == "y"
    )
    if "cloud_sql_proxy" not in os.listdir(curr):
        print(
            "Follow the instructions at https://cloud.google.com/sql/docs/mysql/sql-proxy to install the proxy "
            "in the root directory of the repository, then try again."
        )
        exit(0)
    database_url = sqlalchemy.engine.url.URL(
        drivername="mysql+pymysql",
        username="******",
        password=get_secret(secret_name="DATABASE_PW"),
        host="127.0.0.1",
        port=3307,
        database=app,
    ).__to_string__(hide_password=False)
else:
    database_url = getenv("DATABASE_URL")

engine = sqlalchemy.create_engine(
    database_url,
    **(
        {}
        if use_devdb
        else dict(pool_size=5, max_overflow=2, pool_timeout=30, pool_recycle=1800)
    )
)
コード例 #21
0
ファイル: ide.py プロジェクト: cmcmahon226/cs61a-apps
from common.rpc.hosted import add_domain
from common.rpc.secrets import get_secret
from common.shell_utils import sh
from common.html import html
from common.url_for import get_host, url_for
from common.db import connect_db

NGINX_PORT = os.environ.get("PORT", "8001")
DEFAULT_USER = "******"
SK_RETURN_TO = "start_kill_return_to"

app = Flask(__name__)

create_oauth_client(app,
                    "61a-ide",
                    secret_key=get_secret(secret_name="OKPY_IDE_SECRET"))

with connect_db() as db:
    db("""CREATE TABLE IF NOT EXISTS ide (
    username varchar(128),
    initialized boolean, -- this is unused in the ide context, for now
    locked boolean
);""")

VSCODE_ASSOC = """
{
    "files.associations": {
        "BUILD": "python",
        "WORKSPACE": "python"
    },
    "files.trimTrailingWhitespace": true
コード例 #22
0
def create_oauth_client(
    app: flask.Flask,
    consumer_key,
    secret_key=None,
    success_callback=None,
    return_response=None,
):
    """Add Okpy OAuth for ``consumer_key`` to the current ``app``.

    Specifically, adds an endpoint ``/oauth/login`` that redirects to the Okpy
    login process, ``/oauth/authorized`` that receives the successful result
    of authentication, ``/api/user`` that acts as a test endpoint, and a
    :meth:`~flask_oauthlib.client.OAuthRemoteApp.tokengetter`.

    :param app: the app to add OAuth endpoints to
    :type app: ~flask.Flask

    :param consumer_key: the OAuth client consumer key
    :type consumer_key: str

    :param secret_key: the OAuth client secret, inferred using
        :func:`~common.rpc.secrets.get_secret` if omitted
    :type secret_key: str

    :param success_callback: an optional function to call upon login
    :type success_callback: func

    :param return_response: an optional function to send the OAuth response to
    :type return_response: func
    """
    oauth = OAuth(app)

    if os.getenv("ENV") == "prod":
        if secret_key is None:
            app.secret_key = get_secret(secret_name="OKPY_OAUTH_SECRET")
        else:
            app.secret_key = secret_key
    else:
        consumer_key = "local-dev-all"
        app.secret_key = "kmSPJYPzKJglOOOmr7q0irMfBVMRFXN"

    if not app.debug:
        app.config.update(
            SESSION_COOKIE_SECURE=True,
            SESSION_COOKIE_HTTPONLY=True,
            SESSION_COOKIE_SAMESITE="Lax",
        )

    remote = oauth.remote_app(
        "ok-server",  # Server Name
        consumer_key=consumer_key,
        consumer_secret=app.secret_key,
        request_token_params={
            "scope": "all",
            "state": lambda: security.gen_salt(10)
        },
        base_url="https://okpy.org/api/v3/",
        request_token_url=None,
        access_token_method="POST",
        access_token_url="https://okpy.org/oauth/token",
        authorize_url="https://okpy.org/oauth/authorize",
    )

    def check_req(uri, headers, body):
        """ Add access_token to the URL Request. """
        if "access_token" not in uri and session.get("access_token"):
            params = {"access_token": session.get("access_token")[0]}
            url_parts = list(urllib.parse.urlparse(uri))
            query = dict(urllib.parse.parse_qsl(url_parts[4]))
            query.update(params)

            url_parts[4] = urllib.parse.urlencode(query)
            uri = urllib.parse.urlunparse(url_parts)
        return uri, headers, body

    remote.pre_request = check_req

    @app.route("/oauth/login")
    def login():
        if app.debug:
            response = remote.authorize(
                callback=url_for("authorized", _external=True))
        else:
            response = remote.authorize(
                url_for("authorized", _external=True, _scheme="https"))
        return response

    @app.route("/oauth/authorized")
    def authorized():
        resp = remote.authorized_response()
        if resp is None:
            return "Access denied: error=%s" % (request.args["error"])
        if isinstance(resp, dict) and "access_token" in resp:
            session["access_token"] = (resp["access_token"], "")
            if return_response:
                return_response(resp)

        if success_callback:
            success_callback()

        target = session.get(REDIRECT_KEY)
        if target:
            session.pop(REDIRECT_KEY)
            return redirect(target)
        return redirect(url_for("index"))

    @app.route("/api/user", methods=["POST"])
    def client_method():
        if "access_token" not in session:
            abort(401)
        token = session["access_token"][0]
        r = requests.get(
            "https://okpy.org/api/v3/user/?access_token={}".format(token))
        if not r.ok:
            abort(401)
        return jsonify(r.json())

    @remote.tokengetter
    def get_oauth_token():
        return session.get("access_token")

    app.remote = remote
コード例 #23
0
    def _process(self):
        for trigger_word, event in TRIGGER_WORDS.items():
            if trigger_word in self._message.lower():
                break
        else:
            return

        match = re.search(REGEX_TEMPLATE, self._message)

        if not match:
            return

        try:
            repo = match.group("repo")
            pr = int(match.group("path"))
        except ValueError:
            return

        if COURSE_ORGS[repo.split("/")[0]] != self._course:
            return

        users = requests.get("https://slack.com/api/users.list",
                             params={
                                 "token": self._bot_token
                             }).json()

        g = Github(get_secret(secret_name="GITHUB_ACCESS_TOKEN"))
        repo = g.get_repo(repo)
        pr = repo.get_pull(pr)

        if pr.user.login in STAFF_GITHUB:
            github_email = STAFF_GITHUB[pr.user.login]
        else:
            github_email = pr.user.email

        if not github_email:
            return

        for member in users["members"]:
            if member["profile"].get("email") == github_email:
                break
        else:
            return

        for member in users["members"]:
            if member["id"] == self._event["user"]:
                sender_email = member["profile"].get("email")
                break
        else:
            return

        if sender_email == github_email:
            return

        action = "approved" if event == "APPROVE" else "requested changes on"

        pr.create_review(
            body="{} {} this PR via the Slackbot!".format(
                member["profile"].get("real_name_normalized"), action),
            event=event,
        )

        if trigger_word == "lgtm":
            self.reply = ":white_check_mark: Automatically approved on GitHub!"
        elif trigger_word == "sucks":
            self.reply = ":poop: Wow, this really needs changes..."
        else:
            self.reply = ":x: Changes requested on GitHub!"
コード例 #24
0
ファイル: auth.py プロジェクト: cmcmahon226/cs61a-apps
import requests, sys, time
import logging, ssl

from flask import redirect

from common.oauth_client import is_staff
from common.rpc.secrets import get_secret
from common.url_for import url_for
from common.db import connect_db

log = logging.getLogger(__name__)

CLIENT_ID = "grade-display-exports"
CLIENT_SECRET = get_secret(secret_name="OKPY_OAUTH_SECRET")

OAUTH_SCOPE = "all"

TIMEOUT = 10

TOKEN_ENDPOINT = "/oauth/token"

# ---------------------

with connect_db() as db:
    db("""CREATE TABLE IF NOT EXISTS tokens (
    access_token VARCHAR(128),
    expires_at INTEGER,
    refresh_token VARCHAR(128)
)
""")