An atproto PDS written in Go
at v0.5.1 3.1 kB view raw
1package server 2 3import ( 4 "context" 5 "strings" 6 "time" 7 8 "github.com/Azure/go-autorest/autorest/to" 9 "github.com/bluesky-social/indigo/atproto/atcrypto" 10 "github.com/haileyok/cocoon/identity" 11 "github.com/haileyok/cocoon/internal/helpers" 12 "github.com/haileyok/cocoon/models" 13 "github.com/haileyok/cocoon/plc" 14 "github.com/labstack/echo/v4" 15) 16 17type ComAtprotoSignPlcOperationRequest struct { 18 Token string `json:"token"` 19 VerificationMethods *map[string]string `json:"verificationMethods"` 20 RotationKeys *[]string `json:"rotationKeys"` 21 AlsoKnownAs *[]string `json:"alsoKnownAs"` 22 Services *map[string]identity.OperationService `json:"services"` 23} 24 25type ComAtprotoSignPlcOperationResponse struct { 26 Operation plc.Operation `json:"operation"` 27} 28 29func (s *Server) handleSignPlcOperation(e echo.Context) error { 30 repo := e.Get("repo").(*models.RepoActor) 31 32 var req ComAtprotoSignPlcOperationRequest 33 if err := e.Bind(&req); err != nil { 34 s.logger.Error("error binding", "error", err) 35 return helpers.ServerError(e, nil) 36 } 37 38 if !strings.HasPrefix(repo.Repo.Did, "did:plc:") { 39 return helpers.InputError(e, nil) 40 } 41 42 if repo.PlcOperationCode == nil || repo.PlcOperationCodeExpiresAt == nil { 43 return helpers.InputError(e, to.StringPtr("InvalidToken")) 44 } 45 46 if *repo.PlcOperationCode != req.Token { 47 return helpers.InvalidTokenError(e) 48 } 49 50 if time.Now().UTC().After(*repo.PlcOperationCodeExpiresAt) { 51 return helpers.ExpiredTokenError(e) 52 } 53 54 ctx := context.WithValue(e.Request().Context(), "skip-cache", true) 55 log, err := identity.FetchDidAuditLog(ctx, nil, repo.Repo.Did) 56 if err != nil { 57 s.logger.Error("error fetching doc", "error", err) 58 return helpers.ServerError(e, nil) 59 } 60 61 latest := log[len(log)-1] 62 63 op := plc.Operation{ 64 Type: "plc_operation", 65 VerificationMethods: latest.Operation.VerificationMethods, 66 RotationKeys: latest.Operation.RotationKeys, 67 AlsoKnownAs: latest.Operation.AlsoKnownAs, 68 Services: latest.Operation.Services, 69 Prev: &latest.Cid, 70 } 71 if req.VerificationMethods != nil { 72 op.VerificationMethods = *req.VerificationMethods 73 } 74 if req.RotationKeys != nil { 75 op.RotationKeys = *req.RotationKeys 76 } 77 if req.AlsoKnownAs != nil { 78 op.AlsoKnownAs = *req.AlsoKnownAs 79 } 80 if req.Services != nil { 81 op.Services = *req.Services 82 } 83 84 k, err := atcrypto.ParsePrivateBytesK256(repo.SigningKey) 85 if err != nil { 86 s.logger.Error("error parsing signing key", "error", err) 87 return helpers.ServerError(e, nil) 88 } 89 90 if err := s.plcClient.SignOp(k, &op); err != nil { 91 s.logger.Error("error signing plc operation", "error", err) 92 return helpers.ServerError(e, nil) 93 } 94 95 if err := s.db.Exec("UPDATE repos SET plc_operation_code = NULL, plc_operation_code_expires_at = NULL WHERE did = ?", nil, repo.Repo.Did).Error; err != nil { 96 s.logger.Error("error updating repo", "error", err) 97 return helpers.ServerError(e, nil) 98 } 99 100 return e.JSON(200, ComAtprotoSignPlcOperationResponse{ 101 Operation: op, 102 }) 103}