github.com/decred/politeia@v1.4.0/politeiad/v1.go (about) 1 // Copyright (c) 2020-2021 The Decred developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 package main 5 6 import ( 7 "encoding/hex" 8 "encoding/json" 9 "errors" 10 "net/http" 11 "time" 12 13 v1 "github.com/decred/politeia/politeiad/api/v1" 14 "github.com/decred/politeia/politeiad/backend" 15 "github.com/decred/politeia/util" 16 ) 17 18 func (p *politeia) getIdentity(w http.ResponseWriter, r *http.Request) { 19 var t v1.Identity 20 decoder := json.NewDecoder(r.Body) 21 if err := decoder.Decode(&t); err != nil { 22 p.respondWithUserError(w, v1.ErrorStatusInvalidRequestPayload, nil) 23 return 24 } 25 26 challenge, err := hex.DecodeString(t.Challenge) 27 if err != nil || len(challenge) != v1.ChallengeSize { 28 p.respondWithUserError(w, v1.ErrorStatusInvalidChallenge, nil) 29 return 30 } 31 response := p.identity.SignMessage(challenge) 32 33 reply := v1.IdentityReply{ 34 PublicKey: hex.EncodeToString(p.identity.Public.Key[:]), 35 Response: hex.EncodeToString(response[:]), 36 } 37 38 util.RespondWithJSON(w, http.StatusOK, reply) 39 } 40 41 func (p *politeia) newRecord(w http.ResponseWriter, r *http.Request) { 42 var t v1.NewRecord 43 decoder := json.NewDecoder(r.Body) 44 if err := decoder.Decode(&t); err != nil { 45 p.respondWithUserError(w, v1.ErrorStatusInvalidRequestPayload, nil) 46 return 47 } 48 49 challenge, err := hex.DecodeString(t.Challenge) 50 if err != nil || len(challenge) != v1.ChallengeSize { 51 log.Errorf("%v newRecord: invalid challenge", remoteAddr(r)) 52 p.respondWithUserError(w, v1.ErrorStatusInvalidChallenge, nil) 53 return 54 } 55 56 log.Infof("New record submitted %v", remoteAddr(r)) 57 58 md := convertFrontendMetadataStream(t.Metadata) 59 files := convertFrontendFiles(t.Files) 60 rm, err := p.backend.New(md, files) 61 if err != nil { 62 // Check for content error. 63 var contentErr backend.ContentVerificationError 64 if errors.As(err, &contentErr) { 65 log.Errorf("%v New record content error: %v", 66 remoteAddr(r), contentErr) 67 p.respondWithUserError(w, contentErr.ErrorCode, 68 contentErr.ErrorContext) 69 return 70 } 71 72 // Generic internal error. 73 errorCode := time.Now().Unix() 74 log.Errorf("%v New record error code %v: %v", remoteAddr(r), 75 errorCode, err) 76 p.respondWithServerError(w, errorCode, err) 77 return 78 } 79 80 // Prepare reply. 81 signature := p.identity.SignMessage([]byte(rm.Merkle + rm.Token)) 82 83 response := p.identity.SignMessage(challenge) 84 reply := v1.NewRecordReply{ 85 Response: hex.EncodeToString(response[:]), 86 CensorshipRecord: v1.CensorshipRecord{ 87 Merkle: rm.Merkle, 88 Token: rm.Token, 89 Signature: hex.EncodeToString(signature[:]), 90 }, 91 } 92 93 log.Infof("New record accepted %v: token %v", remoteAddr(r), 94 reply.CensorshipRecord.Token) 95 96 util.RespondWithJSON(w, http.StatusOK, reply) 97 } 98 99 func (p *politeia) updateRecord(w http.ResponseWriter, r *http.Request, vetted bool) { 100 cmd := "unvetted" 101 if vetted { 102 cmd = "vetted" 103 } 104 105 var t v1.UpdateRecord 106 decoder := json.NewDecoder(r.Body) 107 if err := decoder.Decode(&t); err != nil { 108 p.respondWithUserError(w, v1.ErrorStatusInvalidRequestPayload, 109 nil) 110 return 111 } 112 113 challenge, err := hex.DecodeString(t.Challenge) 114 if err != nil || len(challenge) != v1.ChallengeSize { 115 log.Errorf("%v update %v record: invalid challenge", 116 remoteAddr(r), cmd) 117 p.respondWithUserError(w, v1.ErrorStatusInvalidChallenge, nil) 118 return 119 } 120 121 // Validate token 122 token, err := util.ConvertStringToken(t.Token) 123 if err != nil { 124 p.respondWithUserError(w, v1.ErrorStatusInvalidRequestPayload, 125 nil) 126 return 127 } 128 129 log.Infof("Update %v record submitted %v: %x", cmd, remoteAddr(r), 130 token) 131 132 var record *backend.Record 133 if vetted { 134 record, err = p.backend.UpdateVettedRecord(token, 135 convertFrontendMetadataStream(t.MDAppend), 136 convertFrontendMetadataStream(t.MDOverwrite), 137 convertFrontendFiles(t.FilesAdd), t.FilesDel) 138 } else { 139 record, err = p.backend.UpdateUnvettedRecord(token, 140 convertFrontendMetadataStream(t.MDAppend), 141 convertFrontendMetadataStream(t.MDOverwrite), 142 convertFrontendFiles(t.FilesAdd), t.FilesDel) 143 } 144 if err != nil { 145 if errors.Is(err, backend.ErrRecordFound) { 146 log.Errorf("%v update %v record found: %x", 147 remoteAddr(r), cmd, token) 148 p.respondWithUserError(w, v1.ErrorStatusRecordFound, 149 nil) 150 return 151 } 152 if errors.Is(err, backend.ErrRecordNotFound) { 153 log.Errorf("%v update %v record not found: %x", 154 remoteAddr(r), cmd, token) 155 p.respondWithUserError(w, v1.ErrorStatusRecordFound, 156 nil) 157 return 158 } 159 if errors.Is(err, backend.ErrNoChanges) { 160 log.Errorf("%v update %v record no changes: %x", 161 remoteAddr(r), cmd, token) 162 p.respondWithUserError(w, v1.ErrorStatusNoChanges, nil) 163 return 164 } 165 // Check for content error. 166 var contentErr backend.ContentVerificationError 167 if errors.As(err, &contentErr) { 168 log.Errorf("%v update %v record content error: %v", 169 remoteAddr(r), cmd, contentErr) 170 p.respondWithUserError(w, contentErr.ErrorCode, 171 contentErr.ErrorContext) 172 return 173 } 174 175 // Generic internal error. 176 errorCode := time.Now().Unix() 177 log.Errorf("%v Update %v record error code %v: %v", 178 remoteAddr(r), cmd, errorCode, err) 179 p.respondWithServerError(w, errorCode, err) 180 return 181 } 182 183 // Prepare reply. 184 response := p.identity.SignMessage(challenge) 185 reply := v1.UpdateRecordReply{ 186 Response: hex.EncodeToString(response[:]), 187 } 188 189 log.Infof("Update %v record %v: token %v", cmd, remoteAddr(r), 190 record.RecordMetadata.Token) 191 192 util.RespondWithJSON(w, http.StatusOK, reply) 193 } 194 195 func (p *politeia) updateUnvetted(w http.ResponseWriter, r *http.Request) { 196 p.updateRecord(w, r, false) 197 } 198 199 func (p *politeia) updateVetted(w http.ResponseWriter, r *http.Request) { 200 p.updateRecord(w, r, true) 201 } 202 203 func (p *politeia) updateReadme(w http.ResponseWriter, r *http.Request) { 204 var t v1.UpdateReadme 205 decoder := json.NewDecoder(r.Body) 206 if err := decoder.Decode(&t); err != nil { 207 p.respondWithUserError(w, v1.ErrorStatusInvalidRequestPayload, nil) 208 return 209 } 210 211 challenge, err := hex.DecodeString(t.Challenge) 212 if err != nil || len(challenge) != v1.ChallengeSize { 213 p.respondWithUserError(w, v1.ErrorStatusInvalidChallenge, nil) 214 return 215 } 216 217 response := p.identity.SignMessage(challenge) 218 219 reply := v1.UpdateReadmeReply{ 220 Response: hex.EncodeToString(response[:]), 221 } 222 223 err = p.backend.UpdateReadme(t.Content) 224 if err != nil { 225 errorCode := time.Now().Unix() 226 log.Errorf("Error updating readme: %v", err) 227 p.respondWithServerError(w, errorCode, err) 228 return 229 } 230 231 util.RespondWithJSON(w, http.StatusOK, reply) 232 } 233 234 func (p *politeia) getUnvetted(w http.ResponseWriter, r *http.Request) { 235 var t v1.GetUnvetted 236 decoder := json.NewDecoder(r.Body) 237 if err := decoder.Decode(&t); err != nil { 238 p.respondWithUserError(w, v1.ErrorStatusInvalidRequestPayload, nil) 239 return 240 } 241 242 challenge, err := hex.DecodeString(t.Challenge) 243 if err != nil || len(challenge) != v1.ChallengeSize { 244 p.respondWithUserError(w, v1.ErrorStatusInvalidChallenge, nil) 245 return 246 } 247 response := p.identity.SignMessage(challenge) 248 249 reply := v1.GetUnvettedReply{ 250 Response: hex.EncodeToString(response[:]), 251 } 252 253 // Validate token 254 token, err := util.ConvertStringToken(t.Token) 255 if err != nil { 256 p.respondWithUserError(w, v1.ErrorStatusInvalidRequestPayload, nil) 257 return 258 } 259 260 // Ask backend about the censorship token. 261 bpr, err := p.backend.GetUnvetted(token) 262 if errors.Is(err, backend.ErrRecordNotFound) { 263 reply.Record.Status = v1.RecordStatusNotFound 264 log.Errorf("Get unvetted record %v: token %v not found", 265 remoteAddr(r), t.Token) 266 } else if err != nil { 267 // Generic internal error. 268 errorCode := time.Now().Unix() 269 log.Errorf("%v Get unvetted record error code %v: %v", 270 remoteAddr(r), errorCode, err) 271 272 p.respondWithServerError(w, errorCode, err) 273 return 274 } else { 275 reply.Record = p.convertBackendRecord(*bpr) 276 277 // Double check record bits before sending them off 278 err := v1.Verify(p.identity.Public, 279 reply.Record.CensorshipRecord, reply.Record.Files) 280 if err != nil { 281 // Generic internal error. 282 errorCode := time.Now().Unix() 283 log.Errorf("%v Get unvetted record CORRUPTION "+ 284 "error code %v: %v", remoteAddr(r), errorCode, 285 err) 286 287 p.respondWithServerError(w, errorCode, err) 288 return 289 } 290 291 log.Infof("Get unvetted record %v: token %v", remoteAddr(r), 292 t.Token) 293 } 294 295 util.RespondWithJSON(w, http.StatusOK, reply) 296 } 297 298 func (p *politeia) getVetted(w http.ResponseWriter, r *http.Request) { 299 var t v1.GetVetted 300 decoder := json.NewDecoder(r.Body) 301 if err := decoder.Decode(&t); err != nil { 302 p.respondWithUserError(w, v1.ErrorStatusInvalidRequestPayload, nil) 303 return 304 } 305 306 challenge, err := hex.DecodeString(t.Challenge) 307 if err != nil || len(challenge) != v1.ChallengeSize { 308 p.respondWithUserError(w, v1.ErrorStatusInvalidChallenge, nil) 309 return 310 } 311 response := p.identity.SignMessage(challenge) 312 313 reply := v1.GetVettedReply{ 314 Response: hex.EncodeToString(response[:]), 315 } 316 317 // Validate token 318 token, err := util.ConvertStringToken(t.Token) 319 if err != nil { 320 p.respondWithUserError(w, v1.ErrorStatusInvalidRequestPayload, nil) 321 return 322 } 323 324 // Ask backend about the censorship token. 325 bpr, err := p.backend.GetVetted(token, t.Version) 326 if errors.Is(err, backend.ErrRecordNotFound) { 327 reply.Record.Status = v1.RecordStatusNotFound 328 log.Errorf("Get vetted record %v: token %v not found", 329 remoteAddr(r), t.Token) 330 } else if err != nil { 331 // Generic internal error. 332 errorCode := time.Now().Unix() 333 log.Errorf("%v Get vetted record error code %v: %v", 334 remoteAddr(r), errorCode, err) 335 336 p.respondWithServerError(w, errorCode, err) 337 return 338 } else { 339 reply.Record = p.convertBackendRecord(*bpr) 340 341 // Double check record bits before sending them off 342 err := v1.Verify(p.identity.Public, 343 reply.Record.CensorshipRecord, reply.Record.Files) 344 if err != nil { 345 // Generic internal error. 346 errorCode := time.Now().Unix() 347 log.Errorf("%v Get vetted record CORRUPTION "+ 348 "error code %v: %v", remoteAddr(r), errorCode, 349 err) 350 351 p.respondWithServerError(w, errorCode, err) 352 return 353 } 354 log.Infof("Get vetted record %v: token %v", remoteAddr(r), 355 t.Token) 356 } 357 358 util.RespondWithJSON(w, http.StatusOK, reply) 359 } 360 361 func (p *politeia) inventory(w http.ResponseWriter, r *http.Request) { 362 var i v1.Inventory 363 decoder := json.NewDecoder(r.Body) 364 if err := decoder.Decode(&i); err != nil { 365 p.respondWithUserError(w, v1.ErrorStatusInvalidRequestPayload, nil) 366 return 367 } 368 369 challenge, err := hex.DecodeString(i.Challenge) 370 if err != nil || len(challenge) != v1.ChallengeSize { 371 p.respondWithUserError(w, v1.ErrorStatusInvalidChallenge, nil) 372 return 373 } 374 response := p.identity.SignMessage(challenge) 375 376 reply := v1.InventoryReply{ 377 Response: hex.EncodeToString(response[:]), 378 } 379 380 // Ask backend for inventory 381 prs, brs, err := p.backend.Inventory(i.VettedCount, i.VettedStart, i.BranchesCount, 382 i.IncludeFiles, i.AllVersions) 383 if err != nil { 384 // Generic internal error. 385 errorCode := time.Now().Unix() 386 log.Errorf("%v Inventory error code %v: %v", remoteAddr(r), 387 errorCode, err) 388 389 p.respondWithServerError(w, errorCode, err) 390 return 391 } 392 393 // Convert backend records 394 vetted := make([]v1.Record, 0, len(prs)) 395 for _, v := range prs { 396 vetted = append(vetted, p.convertBackendRecord(v)) 397 } 398 reply.Vetted = vetted 399 400 // Convert branches 401 unvetted := make([]v1.Record, 0, len(brs)) 402 for _, v := range brs { 403 unvetted = append(unvetted, p.convertBackendRecord(v)) 404 } 405 reply.Branches = unvetted 406 407 util.RespondWithJSON(w, http.StatusOK, reply) 408 } 409 410 func (p *politeia) setVettedStatus(w http.ResponseWriter, r *http.Request) { 411 var t v1.SetVettedStatus 412 decoder := json.NewDecoder(r.Body) 413 if err := decoder.Decode(&t); err != nil { 414 p.respondWithUserError(w, v1.ErrorStatusInvalidRequestPayload, nil) 415 return 416 } 417 418 challenge, err := hex.DecodeString(t.Challenge) 419 if err != nil || len(challenge) != v1.ChallengeSize { 420 p.respondWithUserError(w, v1.ErrorStatusInvalidChallenge, nil) 421 return 422 } 423 response := p.identity.SignMessage(challenge) 424 425 // Validate token 426 token, err := util.ConvertStringToken(t.Token) 427 if err != nil { 428 p.respondWithUserError(w, v1.ErrorStatusInvalidRequestPayload, nil) 429 return 430 } 431 432 // Ask backend to update status 433 record, err := p.backend.SetVettedStatus(token, 434 convertFrontendStatus(t.Status), 435 convertFrontendMetadataStream(t.MDAppend), 436 convertFrontendMetadataStream(t.MDOverwrite)) 437 if err != nil { 438 // Check for specific errors 439 if errors.Is(err, backend.ErrRecordNotFound) { 440 log.Errorf("%v updateStatus record not "+ 441 "found: %x", remoteAddr(r), token) 442 p.respondWithUserError(w, v1.ErrorStatusRecordFound, 443 nil) 444 return 445 } 446 var serr backend.StateTransitionError 447 if errors.As(err, &serr) { 448 log.Errorf("%v %v %v", remoteAddr(r), t.Token, err) 449 p.respondWithUserError(w, v1.ErrorStatusInvalidRecordStatusTransition, nil) 450 return 451 } 452 // Generic internal error. 453 errorCode := time.Now().Unix() 454 log.Errorf("%v Set status error code %v: %v", 455 remoteAddr(r), errorCode, err) 456 457 p.respondWithServerError(w, errorCode, err) 458 return 459 } 460 461 // Prepare reply. 462 reply := v1.SetVettedStatusReply{ 463 Response: hex.EncodeToString(response[:]), 464 } 465 466 s := convertBackendStatus(record.RecordMetadata.Status) 467 log.Infof("Set vetted record status %v: token %v status %v", 468 remoteAddr(r), t.Token, v1.RecordStatus[s]) 469 470 util.RespondWithJSON(w, http.StatusOK, reply) 471 } 472 473 func (p *politeia) setUnvettedStatus(w http.ResponseWriter, r *http.Request) { 474 var t v1.SetUnvettedStatus 475 decoder := json.NewDecoder(r.Body) 476 if err := decoder.Decode(&t); err != nil { 477 p.respondWithUserError(w, v1.ErrorStatusInvalidRequestPayload, nil) 478 return 479 } 480 481 challenge, err := hex.DecodeString(t.Challenge) 482 if err != nil || len(challenge) != v1.ChallengeSize { 483 p.respondWithUserError(w, v1.ErrorStatusInvalidChallenge, nil) 484 return 485 } 486 response := p.identity.SignMessage(challenge) 487 488 // Validate token 489 token, err := util.ConvertStringToken(t.Token) 490 if err != nil { 491 p.respondWithUserError(w, v1.ErrorStatusInvalidRequestPayload, nil) 492 return 493 } 494 495 // Ask backend to update unvetted status 496 record, err := p.backend.SetUnvettedStatus(token, 497 convertFrontendStatus(t.Status), 498 convertFrontendMetadataStream(t.MDAppend), 499 convertFrontendMetadataStream(t.MDOverwrite)) 500 if err != nil { 501 // Check for specific errors 502 if errors.Is(err, backend.ErrRecordNotFound) { 503 log.Errorf("%v updateUnvettedStatus record not "+ 504 "found: %x", remoteAddr(r), token) 505 p.respondWithUserError(w, v1.ErrorStatusRecordFound, 506 nil) 507 return 508 } 509 var serr backend.StateTransitionError 510 if errors.As(err, &serr) { 511 log.Errorf("%v %v %v", remoteAddr(r), t.Token, err) 512 p.respondWithUserError(w, v1.ErrorStatusInvalidRecordStatusTransition, nil) 513 return 514 } 515 // Generic internal error. 516 errorCode := time.Now().Unix() 517 log.Errorf("%v Set unvetted status error code %v: %v", 518 remoteAddr(r), errorCode, err) 519 520 p.respondWithServerError(w, errorCode, err) 521 return 522 } 523 524 // Prepare reply. 525 reply := v1.SetUnvettedStatusReply{ 526 Response: hex.EncodeToString(response[:]), 527 } 528 529 s := convertBackendStatus(record.RecordMetadata.Status) 530 log.Infof("Set unvetted record status %v: token %v status %v", 531 remoteAddr(r), t.Token, v1.RecordStatus[s]) 532 533 util.RespondWithJSON(w, http.StatusOK, reply) 534 } 535 536 func (p *politeia) updateVettedMetadata(w http.ResponseWriter, r *http.Request) { 537 var t v1.UpdateVettedMetadata 538 decoder := json.NewDecoder(r.Body) 539 if err := decoder.Decode(&t); err != nil { 540 p.respondWithUserError(w, v1.ErrorStatusInvalidRequestPayload, nil) 541 return 542 } 543 544 challenge, err := hex.DecodeString(t.Challenge) 545 if err != nil || len(challenge) != v1.ChallengeSize { 546 p.respondWithUserError(w, v1.ErrorStatusInvalidChallenge, nil) 547 return 548 } 549 response := p.identity.SignMessage(challenge) 550 551 // Validate token 552 token, err := util.ConvertStringToken(t.Token) 553 if err != nil { 554 p.respondWithUserError(w, v1.ErrorStatusInvalidRequestPayload, nil) 555 return 556 } 557 558 log.Infof("Update vetted metadata submitted %v: %x", remoteAddr(r), 559 token) 560 561 err = p.backend.UpdateVettedMetadata(token, 562 convertFrontendMetadataStream(t.MDAppend), 563 convertFrontendMetadataStream(t.MDOverwrite)) 564 if err != nil { 565 if errors.Is(err, backend.ErrNoChanges) { 566 log.Errorf("%v update vetted metadata no changes: %x", 567 remoteAddr(r), token) 568 p.respondWithUserError(w, v1.ErrorStatusNoChanges, nil) 569 return 570 } 571 // Check for content error. 572 var contentErr backend.ContentVerificationError 573 if errors.As(err, &contentErr) { 574 log.Errorf("%v update vetted metadata content error: %v", 575 remoteAddr(r), contentErr) 576 p.respondWithUserError(w, contentErr.ErrorCode, 577 contentErr.ErrorContext) 578 return 579 } 580 581 // Generic internal error. 582 errorCode := time.Now().Unix() 583 log.Errorf("%v Update vetted metadata error code %v: %v", 584 remoteAddr(r), errorCode, err) 585 p.respondWithServerError(w, errorCode, err) 586 return 587 } 588 589 // Reply 590 reply := v1.UpdateVettedMetadataReply{ 591 Response: hex.EncodeToString(response[:]), 592 } 593 594 log.Infof("Update vetted metadata %v: token %x", remoteAddr(r), token) 595 596 util.RespondWithJSON(w, http.StatusOK, reply) 597 } 598 599 func (p *politeia) pluginInventory(w http.ResponseWriter, r *http.Request) { 600 var pi v1.PluginInventory 601 decoder := json.NewDecoder(r.Body) 602 if err := decoder.Decode(&pi); err != nil { 603 p.respondWithUserError(w, v1.ErrorStatusInvalidRequestPayload, 604 nil) 605 return 606 } 607 608 challenge, err := hex.DecodeString(pi.Challenge) 609 if err != nil || len(challenge) != v1.ChallengeSize { 610 p.respondWithUserError(w, v1.ErrorStatusInvalidChallenge, nil) 611 return 612 } 613 response := p.identity.SignMessage(challenge) 614 615 reply := v1.PluginInventoryReply{ 616 Response: hex.EncodeToString(response[:]), 617 } 618 619 plugins, err := p.backend.GetPlugins() 620 if err != nil { 621 // Generic internal error. 622 errorCode := time.Now().Unix() 623 log.Errorf("%v Get plugins error code %v: %v", 624 remoteAddr(r), errorCode, err) 625 p.respondWithServerError(w, errorCode, err) 626 return 627 } 628 for _, v := range plugins { 629 reply.Plugins = append(reply.Plugins, convertBackendPlugin(v)) 630 } 631 632 util.RespondWithJSON(w, http.StatusOK, reply) 633 } 634 635 func (p *politeia) pluginCommand(w http.ResponseWriter, r *http.Request) { 636 var pc v1.PluginCommand 637 decoder := json.NewDecoder(r.Body) 638 if err := decoder.Decode(&pc); err != nil { 639 p.respondWithUserError(w, v1.ErrorStatusInvalidRequestPayload, 640 nil) 641 return 642 } 643 644 challenge, err := hex.DecodeString(pc.Challenge) 645 if err != nil || len(challenge) != v1.ChallengeSize { 646 p.respondWithUserError(w, v1.ErrorStatusInvalidChallenge, nil) 647 return 648 } 649 650 cid, payload, err := p.backend.Plugin(pc.Command, pc.Payload) 651 if err != nil { 652 // Generic internal error. 653 errorCode := time.Now().Unix() 654 log.Errorf("%v %v: backend plugin failed with "+ 655 "command:%v payload:%v err:%v", remoteAddr(r), 656 errorCode, pc.Command, pc.Payload, err) 657 p.respondWithServerError(w, errorCode, err) 658 return 659 } 660 661 response := p.identity.SignMessage(challenge) 662 reply := v1.PluginCommandReply{ 663 Response: hex.EncodeToString(response[:]), 664 ID: pc.ID, 665 Command: cid, 666 CommandID: pc.CommandID, 667 Payload: payload, 668 } 669 670 util.RespondWithJSON(w, http.StatusOK, reply) 671 } 672 673 func convertBackendPluginSetting(bpi backend.PluginSetting) v1.PluginSetting { 674 return v1.PluginSetting{ 675 Key: bpi.Key, 676 Value: bpi.Value, 677 } 678 } 679 680 func convertBackendPlugin(bpi backend.Plugin) v1.Plugin { 681 p := v1.Plugin{ 682 ID: bpi.ID, 683 } 684 for _, v := range bpi.Settings { 685 p.Settings = append(p.Settings, convertBackendPluginSetting(v)) 686 } 687 688 return p 689 } 690 691 // convertBackendMetadataStream converts a backend metadata stream to an API 692 // metadata stream. 693 func convertBackendMetadataStream(mds backend.MetadataStream) v1.MetadataStream { 694 return v1.MetadataStream{ 695 ID: mds.ID, 696 Payload: mds.Payload, 697 } 698 } 699 700 // convertBackendStatus converts a backend MDStatus to an API status. 701 func convertBackendStatus(status backend.MDStatusT) v1.RecordStatusT { 702 s := v1.RecordStatusInvalid 703 switch status { 704 case backend.MDStatusInvalid: 705 s = v1.RecordStatusInvalid 706 case backend.MDStatusUnvetted: 707 s = v1.RecordStatusNotReviewed 708 case backend.MDStatusVetted: 709 s = v1.RecordStatusPublic 710 case backend.MDStatusCensored: 711 s = v1.RecordStatusCensored 712 case backend.MDStatusIterationUnvetted: 713 s = v1.RecordStatusUnreviewedChanges 714 case backend.MDStatusArchived: 715 s = v1.RecordStatusArchived 716 } 717 return s 718 } 719 720 // convertFrontendStatus convert an API status to a backend MDStatus. 721 func convertFrontendStatus(status v1.RecordStatusT) backend.MDStatusT { 722 s := backend.MDStatusInvalid 723 switch status { 724 case v1.RecordStatusInvalid: 725 s = backend.MDStatusInvalid 726 case v1.RecordStatusNotReviewed: 727 s = backend.MDStatusUnvetted 728 case v1.RecordStatusPublic: 729 s = backend.MDStatusVetted 730 case v1.RecordStatusCensored: 731 s = backend.MDStatusCensored 732 case v1.RecordStatusArchived: 733 s = backend.MDStatusArchived 734 } 735 return s 736 } 737 738 func convertFrontendFiles(f []v1.File) []backend.File { 739 files := make([]backend.File, 0, len(f)) 740 for _, v := range f { 741 files = append(files, backend.File{ 742 Name: v.Name, 743 MIME: v.MIME, 744 Digest: v.Digest, 745 Payload: v.Payload, 746 }) 747 } 748 return files 749 } 750 751 func convertFrontendMetadataStream(mds []v1.MetadataStream) []backend.MetadataStream { 752 m := make([]backend.MetadataStream, 0, len(mds)) 753 for _, v := range mds { 754 m = append(m, backend.MetadataStream{ 755 ID: v.ID, 756 Payload: v.Payload, 757 }) 758 } 759 return m 760 } 761 762 func (p *politeia) convertBackendRecord(br backend.Record) v1.Record { 763 rm := br.RecordMetadata 764 765 // Calculate signature 766 signature := p.identity.SignMessage([]byte(rm.Merkle + rm.Token)) 767 768 // Convert MetadataStream 769 md := make([]v1.MetadataStream, 0, len(br.Metadata)) 770 for k := range br.Metadata { 771 md = append(md, convertBackendMetadataStream(br.Metadata[k])) 772 } 773 774 // Convert record 775 pr := v1.Record{ 776 Status: convertBackendStatus(rm.Status), 777 Timestamp: rm.Timestamp, 778 CensorshipRecord: v1.CensorshipRecord{ 779 Merkle: rm.Merkle, 780 Token: rm.Token, 781 Signature: hex.EncodeToString(signature[:]), 782 }, 783 Version: br.Version, 784 Metadata: md, 785 } 786 pr.Files = make([]v1.File, 0, len(br.Files)) 787 for _, v := range br.Files { 788 pr.Files = append(pr.Files, 789 v1.File{ 790 Name: v.Name, 791 MIME: v.MIME, 792 Digest: v.Digest, 793 Payload: v.Payload, 794 }) 795 } 796 797 return pr 798 }