Merge branch 'feat-ip-tracking' into 'main'
Track IP addresses for alternate account discovery See merge request sc07/canvas!15
This commit is contained in:
commit
112f72b4d9
|
@ -21,6 +21,7 @@ Table User {
|
||||||
FactionMember FactionMember [not null]
|
FactionMember FactionMember [not null]
|
||||||
Ban Ban
|
Ban Ban
|
||||||
AuditLog AuditLog [not null]
|
AuditLog AuditLog [not null]
|
||||||
|
IPAddress IPAddress [not null]
|
||||||
}
|
}
|
||||||
|
|
||||||
Table Instance {
|
Table Instance {
|
||||||
|
@ -32,6 +33,18 @@ Table Instance {
|
||||||
Ban Ban
|
Ban Ban
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Table IPAddress {
|
||||||
|
ip String [not null]
|
||||||
|
userSub String [not null]
|
||||||
|
lastUsedAt DateTime [not null]
|
||||||
|
createdAt DateTime [default: `now()`, not null]
|
||||||
|
user User [not null]
|
||||||
|
|
||||||
|
indexes {
|
||||||
|
(ip, userSub) [pk]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Table PaletteColor {
|
Table PaletteColor {
|
||||||
id Int [pk, increment]
|
id Int [pk, increment]
|
||||||
name String [not null]
|
name String [not null]
|
||||||
|
@ -145,6 +158,8 @@ Enum AuditLogAction {
|
||||||
USER_UNADMIN
|
USER_UNADMIN
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ref: IPAddress.userSub > User.sub
|
||||||
|
|
||||||
Ref: Pixel.userId > User.sub
|
Ref: Pixel.userId > User.sub
|
||||||
|
|
||||||
Ref: FactionMember.sub > User.sub
|
Ref: FactionMember.sub > User.sub
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "IPAddress" (
|
||||||
|
"ip" TEXT NOT NULL,
|
||||||
|
"userSub" TEXT NOT NULL,
|
||||||
|
"lastUsedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
CONSTRAINT "IPAddress_pkey" PRIMARY KEY ("ip","userSub")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "IPAddress" ADD CONSTRAINT "IPAddress_userSub_fkey" FOREIGN KEY ("userSub") REFERENCES "User"("sub") ON DELETE RESTRICT ON UPDATE CASCADE;
|
|
@ -36,6 +36,7 @@ model User {
|
||||||
FactionMember FactionMember[]
|
FactionMember FactionMember[]
|
||||||
Ban Ban?
|
Ban Ban?
|
||||||
AuditLog AuditLog[]
|
AuditLog AuditLog[]
|
||||||
|
IPAddress IPAddress[]
|
||||||
}
|
}
|
||||||
|
|
||||||
model Instance {
|
model Instance {
|
||||||
|
@ -47,6 +48,18 @@ model Instance {
|
||||||
Ban Ban?
|
Ban Ban?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model IPAddress {
|
||||||
|
ip String
|
||||||
|
userSub String
|
||||||
|
|
||||||
|
lastUsedAt DateTime
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
|
||||||
|
user User @relation(fields: [userSub], references: [sub])
|
||||||
|
|
||||||
|
@@id([ip, userSub])
|
||||||
|
}
|
||||||
|
|
||||||
model PaletteColor {
|
model PaletteColor {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
name String
|
name String
|
||||||
|
|
|
@ -417,6 +417,61 @@ app.put("/canvas/fill", async (req, res) => {
|
||||||
res.json({ success: true, auditLog });
|
res.json({ success: true, auditLog });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get ip address info
|
||||||
|
*
|
||||||
|
* @query address IP address
|
||||||
|
*/
|
||||||
|
app.get("/ip", async (req, res) => {
|
||||||
|
if (typeof req.query.address !== "string") {
|
||||||
|
return res.status(400).json({ success: false, error: "missing ?address=" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const ip: string = req.query.address;
|
||||||
|
|
||||||
|
const results = await prisma.iPAddress.findMany({
|
||||||
|
select: {
|
||||||
|
userSub: true,
|
||||||
|
createdAt: true,
|
||||||
|
lastUsedAt: true,
|
||||||
|
},
|
||||||
|
where: {
|
||||||
|
ip,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json({ success: true, results });
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all of a user's IP addresses
|
||||||
|
*
|
||||||
|
* @param :sub User ID
|
||||||
|
*/
|
||||||
|
app.get("/user/:sub/ips", async (req, res) => {
|
||||||
|
let user: User;
|
||||||
|
|
||||||
|
try {
|
||||||
|
user = await User.fromSub(req.params.sub);
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof UserNotFound) {
|
||||||
|
res.status(404).json({ success: false, error: "User not found" });
|
||||||
|
} else {
|
||||||
|
Logger.error(`/user/${req.params.sub}/ips Error ` + (e as any)?.message);
|
||||||
|
res.status(500).json({ success: false, error: "Internal error" });
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ips = await prisma.iPAddress.findMany({
|
||||||
|
where: {
|
||||||
|
userSub: user.sub,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json({ success: true, ips });
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create or ban a user
|
* Create or ban a user
|
||||||
*
|
*
|
||||||
|
|
|
@ -164,6 +164,17 @@ export class SocketServer {
|
||||||
);
|
);
|
||||||
|
|
||||||
user?.sockets.add(socket);
|
user?.sockets.add(socket);
|
||||||
|
|
||||||
|
let ip = socket.handshake.address;
|
||||||
|
if (process.env.NODE_ENV === "production") {
|
||||||
|
if (typeof socket.handshake.headers["x-forwarded-for"] === "string") {
|
||||||
|
ip = socket.handshake.headers["x-forwarded-for"];
|
||||||
|
} else {
|
||||||
|
ip = socket.handshake.headers["x-forwarded-for"]?.[0] || ip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
user?.trackIP(ip);
|
||||||
|
|
||||||
Logger.debug("handleConnection " + user?.sockets.size);
|
Logger.debug("handleConnection " + user?.sockets.size);
|
||||||
socket.emit("clearCanvasChunks");
|
socket.emit("clearCanvasChunks");
|
||||||
|
|
||||||
|
|
|
@ -317,6 +317,27 @@ export class User {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async trackIP(ip: string) {
|
||||||
|
await prisma.iPAddress.upsert({
|
||||||
|
where: {
|
||||||
|
ip_userSub: {
|
||||||
|
ip,
|
||||||
|
userSub: this.sub,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
ip,
|
||||||
|
userSub: this.sub,
|
||||||
|
lastUsedAt: new Date(),
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
ip,
|
||||||
|
userSub: this.sub,
|
||||||
|
lastUsedAt: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if this user data is stale and should be updated
|
* Determine if this user data is stale and should be updated
|
||||||
* @see User#update
|
* @see User#update
|
||||||
|
|
Loading…
Reference in New Issue