this repo has no description
1package state 2 3import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "io" 8 "log" 9 "math/rand/v2" 10 "net/http" 11 "path" 12 "strconv" 13 "strings" 14 15 "github.com/bluesky-social/indigo/atproto/identity" 16 securejoin "github.com/cyphar/filepath-securejoin" 17 "github.com/go-chi/chi/v5" 18 "github.com/sotangled/tangled/appview/auth" 19 "github.com/sotangled/tangled/appview/db" 20 "github.com/sotangled/tangled/appview/pages" 21 "github.com/sotangled/tangled/types" 22) 23 24func (s *State) RepoIndex(w http.ResponseWriter, r *http.Request) { 25 ref := chi.URLParam(r, "ref") 26 f, err := fullyResolvedRepo(r) 27 if err != nil { 28 log.Println("failed to fully resolve repo", err) 29 return 30 } 31 var reqUrl string 32 if ref != "" { 33 reqUrl = fmt.Sprintf("http://%s/%s/%s/tree/%s", f.Knot, f.OwnerDid(), f.RepoName, ref) 34 } else { 35 reqUrl = fmt.Sprintf("http://%s/%s/%s", f.Knot, f.OwnerDid(), f.RepoName) 36 } 37 38 resp, err := http.Get(reqUrl) 39 if err != nil { 40 s.pages.Error503(w) 41 log.Println("failed to reach knotserver", err) 42 return 43 } 44 defer resp.Body.Close() 45 46 body, err := io.ReadAll(resp.Body) 47 if err != nil { 48 log.Fatalf("Error reading response body: %v", err) 49 return 50 } 51 52 var result types.RepoIndexResponse 53 err = json.Unmarshal(body, &result) 54 if err != nil { 55 log.Fatalf("Error unmarshalling response body: %v", err) 56 return 57 } 58 59 user := s.auth.GetUser(r) 60 s.pages.RepoIndexPage(w, pages.RepoIndexParams{ 61 LoggedInUser: user, 62 RepoInfo: pages.RepoInfo{ 63 OwnerDid: f.OwnerDid(), 64 OwnerHandle: f.OwnerHandle(), 65 Name: f.RepoName, 66 SettingsAllowed: settingsAllowed(s, user, f), 67 }, 68 RepoIndexResponse: result, 69 }) 70 71 return 72} 73 74func (s *State) RepoLog(w http.ResponseWriter, r *http.Request) { 75 f, err := fullyResolvedRepo(r) 76 if err != nil { 77 log.Println("failed to fully resolve repo", err) 78 return 79 } 80 81 ref := chi.URLParam(r, "ref") 82 resp, err := http.Get(fmt.Sprintf("http://%s/%s/%s/log/%s", f.Knot, f.OwnerDid(), f.RepoName, ref)) 83 if err != nil { 84 log.Println("failed to reach knotserver", err) 85 return 86 } 87 88 body, err := io.ReadAll(resp.Body) 89 if err != nil { 90 log.Fatalf("Error reading response body: %v", err) 91 return 92 } 93 94 var result types.RepoLogResponse 95 err = json.Unmarshal(body, &result) 96 if err != nil { 97 log.Println("failed to parse json response", err) 98 return 99 } 100 101 user := s.auth.GetUser(r) 102 s.pages.RepoLog(w, pages.RepoLogParams{ 103 LoggedInUser: user, 104 RepoInfo: pages.RepoInfo{ 105 OwnerDid: f.OwnerDid(), 106 OwnerHandle: f.OwnerHandle(), 107 Name: f.RepoName, 108 SettingsAllowed: settingsAllowed(s, user, f), 109 }, 110 RepoLogResponse: result, 111 }) 112 return 113} 114 115func (s *State) RepoCommit(w http.ResponseWriter, r *http.Request) { 116 f, err := fullyResolvedRepo(r) 117 if err != nil { 118 log.Println("failed to fully resolve repo", err) 119 return 120 } 121 122 ref := chi.URLParam(r, "ref") 123 resp, err := http.Get(fmt.Sprintf("http://%s/%s/%s/commit/%s", f.Knot, f.OwnerDid(), f.RepoName, ref)) 124 if err != nil { 125 log.Println("failed to reach knotserver", err) 126 return 127 } 128 129 body, err := io.ReadAll(resp.Body) 130 if err != nil { 131 log.Fatalf("Error reading response body: %v", err) 132 return 133 } 134 135 var result types.RepoCommitResponse 136 err = json.Unmarshal(body, &result) 137 if err != nil { 138 log.Println("failed to parse response:", err) 139 return 140 } 141 142 user := s.auth.GetUser(r) 143 s.pages.RepoCommit(w, pages.RepoCommitParams{ 144 LoggedInUser: user, 145 RepoInfo: pages.RepoInfo{ 146 OwnerDid: f.OwnerDid(), 147 OwnerHandle: f.OwnerHandle(), 148 Name: f.RepoName, 149 SettingsAllowed: settingsAllowed(s, user, f), 150 }, 151 RepoCommitResponse: result, 152 }) 153 return 154} 155 156func (s *State) RepoTree(w http.ResponseWriter, r *http.Request) { 157 f, err := fullyResolvedRepo(r) 158 if err != nil { 159 log.Println("failed to fully resolve repo", err) 160 return 161 } 162 163 ref := chi.URLParam(r, "ref") 164 treePath := chi.URLParam(r, "*") 165 resp, err := http.Get(fmt.Sprintf("http://%s/%s/%s/tree/%s/%s", f.Knot, f.OwnerDid(), f.RepoName, ref, treePath)) 166 if err != nil { 167 log.Println("failed to reach knotserver", err) 168 return 169 } 170 171 body, err := io.ReadAll(resp.Body) 172 if err != nil { 173 log.Fatalf("Error reading response body: %v", err) 174 return 175 } 176 177 var result types.RepoTreeResponse 178 err = json.Unmarshal(body, &result) 179 if err != nil { 180 log.Println("failed to parse response:", err) 181 return 182 } 183 184 user := s.auth.GetUser(r) 185 186 var breadcrumbs [][]string 187 breadcrumbs = append(breadcrumbs, []string{f.RepoName, fmt.Sprintf("/%s/%s/tree/%s", f.OwnerDid(), f.RepoName, ref)}) 188 if treePath != "" { 189 for idx, elem := range strings.Split(treePath, "/") { 190 breadcrumbs = append(breadcrumbs, []string{elem, fmt.Sprintf("%s/%s", breadcrumbs[idx][1], elem)}) 191 } 192 } 193 194 baseTreeLink := path.Join(f.OwnerDid(), f.RepoName, "tree", ref, treePath) 195 baseBlobLink := path.Join(f.OwnerDid(), f.RepoName, "blob", ref, treePath) 196 197 s.pages.RepoTree(w, pages.RepoTreeParams{ 198 LoggedInUser: user, 199 BreadCrumbs: breadcrumbs, 200 BaseTreeLink: baseTreeLink, 201 BaseBlobLink: baseBlobLink, 202 RepoInfo: pages.RepoInfo{ 203 OwnerDid: f.OwnerDid(), 204 OwnerHandle: f.OwnerHandle(), 205 Name: f.RepoName, 206 SettingsAllowed: settingsAllowed(s, user, f), 207 }, 208 RepoTreeResponse: result, 209 }) 210 return 211} 212 213func (s *State) RepoTags(w http.ResponseWriter, r *http.Request) { 214 f, err := fullyResolvedRepo(r) 215 if err != nil { 216 log.Println("failed to get repo and knot", err) 217 return 218 } 219 220 resp, err := http.Get(fmt.Sprintf("http://%s/%s/%s/tags", f.Knot, f.OwnerDid(), f.RepoName)) 221 if err != nil { 222 log.Println("failed to reach knotserver", err) 223 return 224 } 225 226 body, err := io.ReadAll(resp.Body) 227 if err != nil { 228 log.Fatalf("Error reading response body: %v", err) 229 return 230 } 231 232 var result types.RepoTagsResponse 233 err = json.Unmarshal(body, &result) 234 if err != nil { 235 log.Println("failed to parse response:", err) 236 return 237 } 238 239 user := s.auth.GetUser(r) 240 s.pages.RepoTags(w, pages.RepoTagsParams{ 241 LoggedInUser: user, 242 RepoInfo: pages.RepoInfo{ 243 OwnerDid: f.OwnerDid(), 244 OwnerHandle: f.OwnerHandle(), 245 Name: f.RepoName, 246 SettingsAllowed: settingsAllowed(s, user, f), 247 }, 248 RepoTagsResponse: result, 249 }) 250 return 251} 252 253func (s *State) RepoBranches(w http.ResponseWriter, r *http.Request) { 254 f, err := fullyResolvedRepo(r) 255 if err != nil { 256 log.Println("failed to get repo and knot", err) 257 return 258 } 259 260 resp, err := http.Get(fmt.Sprintf("http://%s/%s/%s/branches", f.Knot, f.OwnerDid(), f.RepoName)) 261 if err != nil { 262 log.Println("failed to reach knotserver", err) 263 return 264 } 265 266 body, err := io.ReadAll(resp.Body) 267 if err != nil { 268 log.Fatalf("Error reading response body: %v", err) 269 return 270 } 271 272 var result types.RepoBranchesResponse 273 err = json.Unmarshal(body, &result) 274 if err != nil { 275 log.Println("failed to parse response:", err) 276 return 277 } 278 279 user := s.auth.GetUser(r) 280 s.pages.RepoBranches(w, pages.RepoBranchesParams{ 281 LoggedInUser: user, 282 RepoInfo: pages.RepoInfo{ 283 OwnerDid: f.OwnerDid(), 284 OwnerHandle: f.OwnerHandle(), 285 Name: f.RepoName, 286 SettingsAllowed: settingsAllowed(s, user, f), 287 }, 288 RepoBranchesResponse: result, 289 }) 290 return 291} 292 293func (s *State) RepoBlob(w http.ResponseWriter, r *http.Request) { 294 f, err := fullyResolvedRepo(r) 295 if err != nil { 296 log.Println("failed to get repo and knot", err) 297 return 298 } 299 300 ref := chi.URLParam(r, "ref") 301 filePath := chi.URLParam(r, "*") 302 resp, err := http.Get(fmt.Sprintf("http://%s/%s/%s/blob/%s/%s", f.Knot, f.OwnerDid(), f.RepoName, ref, filePath)) 303 if err != nil { 304 log.Println("failed to reach knotserver", err) 305 return 306 } 307 308 body, err := io.ReadAll(resp.Body) 309 if err != nil { 310 log.Fatalf("Error reading response body: %v", err) 311 return 312 } 313 314 var result types.RepoBlobResponse 315 err = json.Unmarshal(body, &result) 316 if err != nil { 317 log.Println("failed to parse response:", err) 318 return 319 } 320 321 var breadcrumbs [][]string 322 breadcrumbs = append(breadcrumbs, []string{f.RepoName, fmt.Sprintf("/%s/%s/tree/%s", f.OwnerDid(), f.RepoName, ref)}) 323 if filePath != "" { 324 for idx, elem := range strings.Split(filePath, "/") { 325 breadcrumbs = append(breadcrumbs, []string{elem, fmt.Sprintf("%s/%s", breadcrumbs[idx][1], elem)}) 326 } 327 } 328 329 user := s.auth.GetUser(r) 330 s.pages.RepoBlob(w, pages.RepoBlobParams{ 331 LoggedInUser: user, 332 RepoInfo: pages.RepoInfo{ 333 OwnerDid: f.OwnerDid(), 334 OwnerHandle: f.OwnerHandle(), 335 Name: f.RepoName, 336 SettingsAllowed: settingsAllowed(s, user, f), 337 }, 338 RepoBlobResponse: result, 339 BreadCrumbs: breadcrumbs, 340 }) 341 return 342} 343 344func (s *State) AddCollaborator(w http.ResponseWriter, r *http.Request) { 345 f, err := fullyResolvedRepo(r) 346 if err != nil { 347 log.Println("failed to get repo and knot", err) 348 return 349 } 350 351 collaborator := r.FormValue("collaborator") 352 if collaborator == "" { 353 http.Error(w, "malformed form", http.StatusBadRequest) 354 return 355 } 356 357 collaboratorIdent, err := s.resolver.ResolveIdent(r.Context(), collaborator) 358 if err != nil { 359 w.Write([]byte("failed to resolve collaborator did to a handle")) 360 return 361 } 362 log.Printf("adding %s to %s\n", collaboratorIdent.Handle.String(), f.Knot) 363 364 // TODO: create an atproto record for this 365 366 secret, err := s.db.GetRegistrationKey(f.Knot) 367 if err != nil { 368 log.Printf("no key found for domain %s: %s\n", f.Knot, err) 369 return 370 } 371 372 ksClient, err := NewSignedClient(f.Knot, secret) 373 if err != nil { 374 log.Println("failed to create client to ", f.Knot) 375 return 376 } 377 378 ksResp, err := ksClient.AddCollaborator(f.OwnerDid(), f.RepoName, collaboratorIdent.DID.String()) 379 if err != nil { 380 log.Printf("failed to make request to %s: %s", f.Knot, err) 381 return 382 } 383 384 if ksResp.StatusCode != http.StatusNoContent { 385 w.Write([]byte(fmt.Sprint("knotserver failed to add collaborator: ", err))) 386 return 387 } 388 389 err = s.enforcer.AddCollaborator(collaboratorIdent.DID.String(), f.Knot, f.OwnerSlashRepo()) 390 if err != nil { 391 w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err))) 392 return 393 } 394 395 err = s.db.AddCollaborator(collaboratorIdent.DID.String(), f.OwnerDid(), f.RepoName, f.Knot) 396 if err != nil { 397 w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err))) 398 return 399 } 400 401 w.Write([]byte(fmt.Sprint("added collaborator: ", collaboratorIdent.Handle.String()))) 402 403} 404 405func (s *State) RepoSettings(w http.ResponseWriter, r *http.Request) { 406 f, err := fullyResolvedRepo(r) 407 if err != nil { 408 log.Println("failed to get repo and knot", err) 409 return 410 } 411 412 switch r.Method { 413 case http.MethodGet: 414 // for now, this is just pubkeys 415 user := s.auth.GetUser(r) 416 repoCollaborators, err := f.Collaborators(r.Context(), s) 417 if err != nil { 418 log.Println("failed to get collaborators", err) 419 } 420 421 isCollaboratorInviteAllowed := false 422 if user != nil { 423 ok, err := s.enforcer.IsCollaboratorInviteAllowed(user.Did, f.Knot, f.OwnerSlashRepo()) 424 if err == nil && ok { 425 isCollaboratorInviteAllowed = true 426 } 427 } 428 429 s.pages.RepoSettings(w, pages.RepoSettingsParams{ 430 LoggedInUser: user, 431 RepoInfo: pages.RepoInfo{ 432 OwnerDid: f.OwnerDid(), 433 OwnerHandle: f.OwnerHandle(), 434 Name: f.RepoName, 435 SettingsAllowed: settingsAllowed(s, user, f), 436 }, 437 Collaborators: repoCollaborators, 438 IsCollaboratorInviteAllowed: isCollaboratorInviteAllowed, 439 }) 440 } 441} 442 443type FullyResolvedRepo struct { 444 Knot string 445 OwnerId identity.Identity 446 RepoName string 447 RepoAt string 448} 449 450func (f *FullyResolvedRepo) OwnerDid() string { 451 return f.OwnerId.DID.String() 452} 453 454func (f *FullyResolvedRepo) OwnerHandle() string { 455 return f.OwnerId.Handle.String() 456} 457 458func (f *FullyResolvedRepo) OwnerSlashRepo() string { 459 p, _ := securejoin.SecureJoin(f.OwnerDid(), f.RepoName) 460 return p 461} 462 463func (f *FullyResolvedRepo) Collaborators(ctx context.Context, s *State) ([]pages.Collaborator, error) { 464 repoCollaborators, err := s.enforcer.E.GetImplicitUsersForResourceByDomain(f.OwnerSlashRepo(), f.Knot) 465 if err != nil { 466 return nil, err 467 } 468 469 var collaborators []pages.Collaborator 470 for _, item := range repoCollaborators { 471 // currently only two roles: owner and member 472 var role string 473 if item[3] == "repo:owner" { 474 role = "owner" 475 } else if item[3] == "repo:collaborator" { 476 role = "collaborator" 477 } else { 478 continue 479 } 480 481 did := item[0] 482 483 c := pages.Collaborator{ 484 Did: did, 485 Handle: "", 486 Role: role, 487 } 488 collaborators = append(collaborators, c) 489 } 490 491 // populate all collborators with handles 492 identsToResolve := make([]string, len(collaborators)) 493 for i, collab := range collaborators { 494 identsToResolve[i] = collab.Did 495 } 496 497 resolvedIdents := s.resolver.ResolveIdents(ctx, identsToResolve) 498 for i, resolved := range resolvedIdents { 499 if resolved != nil { 500 collaborators[i].Handle = resolved.Handle.String() 501 } 502 } 503 504 return collaborators, nil 505} 506 507func (s *State) RepoSingleIssue(w http.ResponseWriter, r *http.Request) { 508 user := s.auth.GetUser(r) 509 f, err := fullyResolvedRepo(r) 510 if err != nil { 511 log.Println("failed to get repo and knot", err) 512 return 513 } 514 515 issueId := chi.URLParam(r, "issue") 516 issueIdInt, err := strconv.Atoi(issueId) 517 if err != nil { 518 http.Error(w, "bad issue id", http.StatusBadRequest) 519 log.Println("failed to parse issue id", err) 520 return 521 } 522 523 issue, comments, err := s.db.GetIssueWithComments(f.RepoAt, issueIdInt) 524 if err != nil { 525 log.Println("failed to get issue and comments", err) 526 s.pages.Notice(w, "issues", "Failed to load issue. Try again later.") 527 return 528 } 529 530 issueOwnerIdent, err := s.resolver.ResolveIdent(r.Context(), issue.OwnerDid) 531 if err != nil { 532 log.Println("failed to resolve issue owner", err) 533 } 534 535 s.pages.RepoSingleIssue(w, pages.RepoSingleIssueParams{ 536 LoggedInUser: user, 537 RepoInfo: pages.RepoInfo{ 538 OwnerDid: f.OwnerDid(), 539 OwnerHandle: f.OwnerHandle(), 540 Name: f.RepoName, 541 SettingsAllowed: settingsAllowed(s, user, f), 542 }, 543 Issue: *issue, 544 Comments: comments, 545 546 IssueOwnerHandle: issueOwnerIdent.Handle.String(), 547 }) 548 549} 550 551func (s *State) CloseIssue(w http.ResponseWriter, r *http.Request) { 552 user := s.auth.GetUser(r) 553 f, err := fullyResolvedRepo(r) 554 if err != nil { 555 log.Println("failed to get repo and knot", err) 556 return 557 } 558 559 issueId := chi.URLParam(r, "issue") 560 issueIdInt, err := strconv.Atoi(issueId) 561 if err != nil { 562 http.Error(w, "bad issue id", http.StatusBadRequest) 563 log.Println("failed to parse issue id", err) 564 return 565 } 566 567 if user.Did == f.OwnerDid() { 568 err := s.db.CloseIssue(f.RepoAt, issueIdInt) 569 if err != nil { 570 log.Println("failed to close issue", err) 571 s.pages.Notice(w, "issues", "Failed to close issue. Try again later.") 572 return 573 } 574 s.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueIdInt)) 575 return 576 } else { 577 log.Println("user is not the owner of the repo") 578 http.Error(w, "for biden", http.StatusUnauthorized) 579 return 580 } 581} 582 583func (s *State) ReopenIssue(w http.ResponseWriter, r *http.Request) { 584 user := s.auth.GetUser(r) 585 f, err := fullyResolvedRepo(r) 586 if err != nil { 587 log.Println("failed to get repo and knot", err) 588 return 589 } 590 591 issueId := chi.URLParam(r, "issue") 592 issueIdInt, err := strconv.Atoi(issueId) 593 if err != nil { 594 http.Error(w, "bad issue id", http.StatusBadRequest) 595 log.Println("failed to parse issue id", err) 596 return 597 } 598 599 if user.Did == f.OwnerDid() { 600 err := s.db.ReopenIssue(f.RepoAt, issueIdInt) 601 if err != nil { 602 log.Println("failed to reopen issue", err) 603 s.pages.Notice(w, "issues", "Failed to reopen issue. Try again later.") 604 return 605 } 606 s.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueIdInt)) 607 return 608 } else { 609 log.Println("user is not the owner of the repo") 610 http.Error(w, "forbidden", http.StatusUnauthorized) 611 return 612 } 613} 614 615func (s *State) IssueComment(w http.ResponseWriter, r *http.Request) { 616 user := s.auth.GetUser(r) 617 f, err := fullyResolvedRepo(r) 618 if err != nil { 619 log.Println("failed to get repo and knot", err) 620 return 621 } 622 623 issueId := chi.URLParam(r, "issue") 624 issueIdInt, err := strconv.Atoi(issueId) 625 if err != nil { 626 http.Error(w, "bad issue id", http.StatusBadRequest) 627 log.Println("failed to parse issue id", err) 628 return 629 } 630 631 switch r.Method { 632 case http.MethodPost: 633 body := r.FormValue("body") 634 if body == "" { 635 s.pages.Notice(w, "issue", "Body is required") 636 return 637 } 638 639 commentId := rand.IntN(1000000) 640 fmt.Println(commentId) 641 fmt.Println("comment id", commentId) 642 643 err := s.db.NewComment(&db.Comment{ 644 OwnerDid: user.Did, 645 RepoAt: f.RepoAt, 646 Issue: issueIdInt, 647 CommentId: commentId, 648 Body: body, 649 }) 650 if err != nil { 651 log.Println("failed to create comment", err) 652 s.pages.Notice(w, "issue-comment", "Failed to create comment.") 653 return 654 } 655 656 s.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d#comment-%d", f.OwnerSlashRepo(), issueIdInt, commentId)) 657 return 658 } 659} 660 661func (s *State) RepoIssues(w http.ResponseWriter, r *http.Request) { 662 user := s.auth.GetUser(r) 663 f, err := fullyResolvedRepo(r) 664 if err != nil { 665 log.Println("failed to get repo and knot", err) 666 return 667 } 668 669 issues, err := s.db.GetIssues(f.RepoAt) 670 if err != nil { 671 log.Println("failed to get issues", err) 672 s.pages.Notice(w, "issues", "Failed to load issues. Try again later.") 673 return 674 } 675 676 s.pages.RepoIssues(w, pages.RepoIssuesParams{ 677 LoggedInUser: s.auth.GetUser(r), 678 RepoInfo: pages.RepoInfo{ 679 OwnerDid: f.OwnerDid(), 680 OwnerHandle: f.OwnerHandle(), 681 Name: f.RepoName, 682 SettingsAllowed: settingsAllowed(s, user, f), 683 }, 684 Issues: issues, 685 }) 686 return 687} 688 689func (s *State) NewIssue(w http.ResponseWriter, r *http.Request) { 690 user := s.auth.GetUser(r) 691 692 f, err := fullyResolvedRepo(r) 693 if err != nil { 694 log.Println("failed to get repo and knot", err) 695 return 696 } 697 698 switch r.Method { 699 case http.MethodGet: 700 s.pages.RepoNewIssue(w, pages.RepoNewIssueParams{ 701 LoggedInUser: user, 702 RepoInfo: pages.RepoInfo{ 703 Name: f.RepoName, 704 OwnerDid: f.OwnerDid(), 705 OwnerHandle: f.OwnerHandle(), 706 SettingsAllowed: settingsAllowed(s, user, f), 707 }, 708 }) 709 case http.MethodPost: 710 title := r.FormValue("title") 711 body := r.FormValue("body") 712 713 if title == "" || body == "" { 714 s.pages.Notice(w, "issue", "Title and body are required") 715 return 716 } 717 718 issueId, err := s.db.NewIssue(&db.Issue{ 719 RepoAt: f.RepoAt, 720 Title: title, 721 Body: body, 722 OwnerDid: user.Did, 723 }) 724 if err != nil { 725 log.Println("failed to create issue", err) 726 s.pages.Notice(w, "issue", "Failed to create issue.") 727 return 728 } 729 730 s.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueId)) 731 return 732 } 733} 734 735func fullyResolvedRepo(r *http.Request) (*FullyResolvedRepo, error) { 736 repoName := chi.URLParam(r, "repo") 737 knot, ok := r.Context().Value("knot").(string) 738 if !ok { 739 log.Println("malformed middleware") 740 return nil, fmt.Errorf("malformed middleware") 741 } 742 id, ok := r.Context().Value("resolvedId").(identity.Identity) 743 if !ok { 744 log.Println("malformed middleware") 745 return nil, fmt.Errorf("malformed middleware") 746 } 747 748 repoAt, ok := r.Context().Value("repoAt").(string) 749 if !ok { 750 log.Println("malformed middleware") 751 return nil, fmt.Errorf("malformed middleware") 752 } 753 754 return &FullyResolvedRepo{ 755 Knot: knot, 756 OwnerId: id, 757 RepoName: repoName, 758 RepoAt: repoAt, 759 }, nil 760} 761 762func settingsAllowed(s *State, u *auth.User, f *FullyResolvedRepo) bool { 763 settingsAllowed := false 764 if u != nil { 765 ok, err := s.enforcer.IsSettingsAllowed(u.Did, f.Knot, f.OwnerSlashRepo()) 766 if err == nil && ok { 767 settingsAllowed = true 768 } else { 769 log.Println(err, ok) 770 } 771 } 772 773 return settingsAllowed 774}