Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Add object ownership management #23

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions backend/src/drivers/sqlite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ const initSchema = async (db: Database) => {
await db.run(
"CREATE TABLE IF NOT EXISTS transactionResults (cid TEXT PRIMARY KEY, transaction_result TEXT, head_cid TEXT)"
);

// Object Ownership
await db.exec(
"CREATE TABLE IF NOT EXISTS object_ownership (cid TEXT PRIMARY KEY, user_id TEXT, is_admin BOOLEAN, marked_as_deleted BOOLEAN)"
);
};

const createDB = async (
Expand Down
1 change: 1 addition & 0 deletions backend/src/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from "./chunkInfo.js";
export * from "./folderTree.js";
export * from "./nodeWithMetadata.js";
export * from "./transaction.js";
export * from "./user.js";
4 changes: 4 additions & 0 deletions backend/src/models/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type User = {
provider: string;
email: string;
};
1 change: 1 addition & 0 deletions backend/src/repositories/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { getDatabase } from "../drivers/sqlite.js";
export { metadataRepository } from "./metadata.js";
export { nodesRepository } from "./nodes.js";
export { ownershipRepository } from "./ownership.js";
export { transactionResultsRepository } from "./transactionResults.js";
42 changes: 42 additions & 0 deletions backend/src/repositories/ownership.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { getDatabase } from "../drivers/sqlite.js";

const setUserAsOwner = async (cid: string, userId: string) => {
const db = await getDatabase();

await db.run(
"INSERT INTO object_ownership (cid, user_id, is_admin, marked_as_deleted) VALUES (?, ?, ?, ?)",
cid,
userId,
false,
false
);
};

const setUserAsAdmin = async (cid: string, userId: string) => {
const db = await getDatabase();

await db.run(
"INSERT INTO object_ownership (cid, user_id, is_admin, marked_as_deleted) VALUES (?, ?, ?, ?)",
cid,
userId,
true,
false
);
};

const setObjectAsDeleted = async (userId: string, cid: string) => {
const db = await getDatabase();

await db.run(
"UPDATE object_ownership SET marked_as_deleted = ? WHERE cid = ? AND user_id = ?",
true,
cid,
userId
);
};

export const ownershipRepository = {
setUserAsOwner,
setUserAsAdmin,
setObjectAsDeleted,
};
19 changes: 18 additions & 1 deletion backend/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { decode } from "@ipld/dag-pb";
import "dotenv/config.js";
import { FolderTreeSchema, NodeWithMetadata } from "./models/index.js";
import { transactionResultsRepository } from "./repositories/index.js";
import { handleAuth } from "./services/authManager/express.js";
import { uploadManager } from "./services/uploadManager/index.js";
import {
FilesUseCases,
Expand Down Expand Up @@ -55,8 +56,18 @@ const createServer = async () => {
return res.status(400).json({ error: "Field `data` is required" });
}

const user = await handleAuth(req, res);
if (!user) {
return;
}

const buffer = Buffer.from(data, "base64");
const cid = await FilesUseCases.uploadFile(buffer, filename, mimeType);
const cid = await FilesUseCases.uploadFile(
user,
buffer,
filename,
mimeType
);

res.json({ cid });
} catch (error) {
Expand All @@ -67,6 +78,11 @@ const createServer = async () => {

app.post("/upload-folder", multer().any(), async (req, res) => {
try {
const user = await handleAuth(req, res);
if (!user) {
return;
}

const folderTreeString = req.body.folderTree;
if (!folderTreeString) {
return res
Expand All @@ -90,6 +106,7 @@ const createServer = async () => {
}

const cid = await FilesUseCases.uploadTree(
user,
validatedFolderTree.data,
(req.files || []) as Express.Multer.File[]
);
Expand Down
31 changes: 31 additions & 0 deletions backend/src/services/authManager/express.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Request, Response } from "express";
import { AuthManager } from "./index.js";

export const handleAuth = async (req: Request, res: Response) => {
const accessToken = req.headers.authorization?.split(" ")[1];
if (!accessToken) {
res.status(401).json({ error: "Missing or invalid access token" });
return null;
}

const provider = req.headers["x-auth-provider"];
if (!provider || typeof provider !== "string") {
res
.status(401)
.json({ error: "Missing or invalid x-auth-provider header" });

return null;
}

const user = await AuthManager.getUserFromAccessToken(
provider,
accessToken
).catch(() => null);

if (!user) {
res.status(401).json({ error: "Failed to authenticate user" });
return null;
}

return user;
};
18 changes: 18 additions & 0 deletions backend/src/services/authManager/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { User } from "../../models/index.js";
import { GoogleAuth } from "./providers/index.js";

const getUserFromAccessToken = async (
provider: string,
accessToken: string
): Promise<User> => {
switch (provider) {
case "google":
return GoogleAuth.getUserFromAccessToken(accessToken);
default:
throw new Error("Invalid provider");
}
};

export const AuthManager = {
getUserFromAccessToken,
};
34 changes: 34 additions & 0 deletions backend/src/services/authManager/providers/google.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { User } from "../../../models/index.js";

type GoogleUser = {
email: string;
id: string;
verified_email: boolean;
picture: string;
hd: string;
};

const getUserFromAccessToken = async (accessToken: string): Promise<User> => {
const googleUser = await fetch(
"https://www.googleapis.com/oauth2/v1/userinfo?scope=https://www.googleapis.com/auth/userinfo.email",
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
}
).then((res) => {
if (res.status >= 400) {
throw new Error("Failed to fetch user info");
}
return res.json() as Promise<GoogleUser>;
});

return {
provider: "google",
email: googleUser.email,
};
};

export const GoogleAuth = {
getUserFromAccessToken,
};
1 change: 1 addition & 0 deletions backend/src/services/authManager/providers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./google.js";
11 changes: 10 additions & 1 deletion backend/src/useCases/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ import {
} from "@autonomys/auto-drive";
import { PBNode } from "@ipld/dag-pb";
import { FolderTree } from "../models/index.js";
import { MetadataUseCases, NodesUseCases } from "../useCases/index.js";
import { User } from "../models/user.js";
import {
MetadataUseCases,
NodesUseCases,
OwnershipUseCases,
} from "../useCases/index.js";

const processFile = async (
data: Buffer,
Expand Down Expand Up @@ -140,24 +145,28 @@ const retrieveAndReassembleData = async (
};

const uploadFile = async (
user: User,
data: Buffer,
filename?: string,
mimeType?: string
): Promise<string> => {
const { cid, nodes } = await processFile(data, filename, mimeType);

await NodesUseCases.saveNodesWithHeadCID(nodes, cid);
await OwnershipUseCases.setUserAsOwner(user, cid);

return cid;
};

const uploadTree = async (
user: User,
folderTree: FolderTree,
files: Express.Multer.File[]
): Promise<string> => {
const { cid, nodes } = await processTree(folderTree, files);

await NodesUseCases.saveNodesWithHeadCID(nodes, cid);
await OwnershipUseCases.setUserAsOwner(user, cid);

return cid;
};
Expand Down
1 change: 1 addition & 0 deletions backend/src/useCases/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from "./files.js";
export * from "./metadata.js";
export * from "./nodes.js";
export * from "./ownership.js";
export * from "./transactionResults.js";
20 changes: 20 additions & 0 deletions backend/src/useCases/ownership.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { User } from "../models/index.js";
import { ownershipRepository } from "../repositories/index.js";

const setUserAsOwner = async (user: User, cid: string) => {
await ownershipRepository.setUserAsOwner(cid, user.email);
};

const setUserAsAdmin = async (user: User, cid: string) => {
await ownershipRepository.setUserAsAdmin(cid, user.email);
};

const setObjectAsDeleted = async (user: User, cid: string) => {
await ownershipRepository.setObjectAsDeleted(user.email, cid);
};

export const OwnershipUseCases = {
setUserAsOwner,
setUserAsAdmin,
setObjectAsDeleted,
};
Binary file modified frontend/.yarn/install-state.gz
Binary file not shown.
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"lucide-react": "^0.439.0",
"multiformats": "^13.2.3",
"next": "^14.2.10",
"next-auth": "^4.24.8",
"react": "^18",
"react-dom": "^18",
"usehooks-ts": "^3.1.0",
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FileCard } from "../../../components/common/FileCard";
import { ApiService } from "../../../services/api";
import { FileCard } from "../../../../components/common/FileCard";
import { ApiService } from "../../../../services/api";

export default async function Page({ params }: { params: { cid: string } }) {
const { cid } = params;
Expand Down
Loading