.ts
TypeScript
(application/typescript)
// 1st-party
import type { ServiceMethodFactory } from "@ethicdevs/react-monolith";
import { GitServer } from "@ethicdevs/fastify-git-server";
// app
import { GitServerServiceDeps } from "./types";
import { ResourceVisibility } from "@prisma/client";

const makeAuthorizationResolver: ServiceMethodFactory<
  GitServerServiceDeps,
  [string, GitServer.AuthCredentials],
  PromiseLike<boolean>
> = ({ cryptoService, request }) => {
  return async (repoPath, { username, password }) => {
    const [orgSlug, repoSlugUnsafe] = repoPath.split("/");

    if (orgSlug == null || orgSlug.trim() === "") {
      return false;
    }

    if (repoSlugUnsafe == null || repoSlugUnsafe.trim() === "") {
      return false;
    }

    // if password contains the publicKey instead of regular password
    const isPubKeyAuth = repoSlugUnsafe.endsWith(".pub");
    const repoSlug = repoSlugUnsafe.replace(/\.(git|pub)$/, "");

    console.log("AuthorizationResolver called with:", {
      repoPath,
      username,
      isPubKeyAuth,
    });

    const user = await request.prisma.user.findUnique({
      where: {
        username,
      },
    });

    if (user == null) {
      return false;
    }

    const org = await request.prisma.organization.findUnique({
      include: {
        owner: true,
        memberships: true,
      },
      where: {
        slug: orgSlug,
      },
    });

    if (org == null) {
      return false;
    }

    const repo = await request.prisma.repository.findFirst({
      where: {
        slug: repoSlug,
        organization: {
          slug: orgSlug,
        },
      },
    });

    if (repo == null) {
      return false;
    }

    const repoOrgOwnerIsReqUser = org.ownerId === user.id;
    const repoMembershipsHasReqUser =
      org.memberships.find((m) => m.id === user.id) != null;

    // allow read-only for unlisted users without auth, but write still behind auth.
    if (repo.visibility === ResourceVisibility.PUBLIC) {
      return true;
    }

    if (
      repoOrgOwnerIsReqUser === false &&
      repoMembershipsHasReqUser === false
    ) {
      return false;
    }

    if (isPubKeyAuth) {
      const matchingPk = await request.prisma.userSSHKey.findFirst({
        where: {
          // password contains the publicKey instead of regular password
          key: password,
          revoked: false,
        },
        include: {
          user: true,
        },
      });

      return (
        matchingPk != null &&
        matchingPk.user.id === user.id &&
        matchingPk.user.username === username
      );
    }

    const hashedPassword = cryptoService.computeHash(password);
    const authed = hashedPassword === user.hashedPassword;

    console.log("git.authorizationResolver:", authed, username);

    return authed;
  };
};

export default makeAuthorizationResolver;