Track IP addresses for alternate account discovery
This commit is contained in:
parent
184492358c
commit
91281ef52e
|
@ -21,6 +21,7 @@ Table User {
|
|||
FactionMember FactionMember [not null]
|
||||
Ban Ban
|
||||
AuditLog AuditLog [not null]
|
||||
IPAddress IPAddress [not null]
|
||||
}
|
||||
|
||||
Table Instance {
|
||||
|
@ -32,6 +33,18 @@ Table Instance {
|
|||
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 {
|
||||
id Int [pk, increment]
|
||||
name String [not null]
|
||||
|
@ -145,6 +158,8 @@ Enum AuditLogAction {
|
|||
USER_UNADMIN
|
||||
}
|
||||
|
||||
Ref: IPAddress.userSub > User.sub
|
||||
|
||||
Ref: Pixel.userId > 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[]
|
||||
Ban Ban?
|
||||
AuditLog AuditLog[]
|
||||
IPAddress IPAddress[]
|
||||
}
|
||||
|
||||
model Instance {
|
||||
|
@ -47,6 +48,18 @@ model Instance {
|
|||
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 {
|
||||
id Int @id @default(autoincrement())
|
||||
name String
|
||||
|
|
|
@ -417,6 +417,61 @@ app.put("/canvas/fill", async (req, res) => {
|
|||
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
|
||||
*
|
||||
|
|
|
@ -164,6 +164,17 @@ export class SocketServer {
|
|||
);
|
||||
|
||||
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);
|
||||
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
|
||||
* @see User#update
|
||||
|
|
Loading…
Reference in New Issue