-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathComAtprotoRepo_GetRecord.cs
More file actions
108 lines (84 loc) · 4.07 KB
/
Copy pathComAtprotoRepo_GetRecord.cs
File metadata and controls
108 lines (84 loc) · 4.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
using System.Text.Json.Nodes;
using dnproto.repo;
using dnproto.ws;
using Microsoft.AspNetCore.Http;
namespace dnproto.pds.xrpc;
public class ComAtprotoRepo_GetRecord : BaseXrpcCommand
{
public IResult GetResponse()
{
IncrementStatistics();
//
// Get params
//
string? repo = HttpContext.Request.Query.ContainsKey("repo") ? (string?) HttpContext.Request.Query["repo"] : null;
string? collection = HttpContext.Request.Query.ContainsKey("collection") ? (string?) HttpContext.Request.Query["collection"] : null;
string? rkey = HttpContext.Request.Query.ContainsKey("rkey") ? (string?) HttpContext.Request.Query["rkey"] : null;
if(string.IsNullOrEmpty(collection) || string.IsNullOrEmpty(rkey))
{
return Results.Json(new { error = "InvalidRequest", message = "Error: Params must have 'collection' and 'rkey'." }, statusCode: 400);
}
// Default to local user if repo not specified
if (string.IsNullOrEmpty(repo))
{
repo = Pds.PdsDb.GetConfigProperty("UserDid");
}
//
// Check if this is a request for a different repo - if so, proxy it
//
bool isLocalRepo = repo == Pds.PdsDb.GetConfigProperty("UserDid") || repo == Pds.PdsDb.GetConfigProperty("UserHandle");
if (!isLocalRepo)
{
Pds.Logger.LogInfo($"Proxying getRecord request for repo: {repo}");
// Resolve the repo (could be DID or handle) to find their PDS
var actorInfo = Pds.LocalFileSystem.ResolveActorInfo(repo);
if (actorInfo == null || string.IsNullOrEmpty(actorInfo.Pds) || string.IsNullOrEmpty(actorInfo.Did))
{
Pds.Logger.LogError($"Unable to resolve actor info for repo: {repo}");
return Results.Json(new { error = "NotFound", message = "Unable to resolve repository" }, statusCode: 404);
}
// Validate PDS hostname (SSRF protection)
if (!IsValidOutboundHost(actorInfo.Pds))
{
Pds.Logger.LogError($"[SECURITY] Blocked invalid or internal PDS hostname: {actorInfo.Pds}");
return Results.Json(new { error = "InvalidRequest", message = "Invalid PDS hostname" }, statusCode: 400);
}
// Proxy the request to the target PDS
string targetUrl = $"https://{actorInfo.Pds}/xrpc/com.atproto.repo.getRecord?repo={Uri.EscapeDataString(actorInfo.Did)}&collection={Uri.EscapeDataString(collection)}&rkey={Uri.EscapeDataString(rkey)}";
Pds.Logger.LogInfo($"Proxying to: {targetUrl}");
try
{
JsonNode? response = BlueskyClient.SendRequest(targetUrl, System.Net.Http.HttpMethod.Get);
if (response == null)
{
return Results.Json(new { error = "NotFound", message = "Record not found" }, statusCode: 404);
}
return Results.Json(response, statusCode: 200);
}
catch (Exception ex)
{
Pds.Logger.LogError($"Error proxying getRecord request: {ex.Message}");
return Results.Json(new { error = "NotFound", message = "Record not found" }, statusCode: 404);
}
}
//
// Retrieve local record
//
bool recordExists = Pds.PdsDb.RecordExists(collection!, rkey!);
if(!recordExists)
{
return Results.Json(new { error = "RecordNotFound", message = "Error: Record not found." }, statusCode: 400);
}
string uri = $"at://{Pds.PdsDb.GetConfigProperty("UserDid")}/{collection}/{rkey}";
RepoRecord repoRecord = Pds.PdsDb.GetRepoRecord(collection!, rkey!);
//
// Return success
//
return Results.Json(new JsonObject
{
["uri"] = uri,
["cid"] = repoRecord.Cid.Base32,
["value"] = repoRecord.JsonString != null ? JsonNode.Parse(repoRecord.JsonString)! : null
}, statusCode: 200);
}
}