| 536 | } |
| 537 | |
| 538 | func createWorkspace( |
| 539 | ctx context.Context, |
| 540 | auditReq *audit.Request[database.WorkspaceTable], |
| 541 | initiatorID uuid.UUID, |
| 542 | api *API, |
| 543 | owner workspaceOwner, |
| 544 | req codersdk.CreateWorkspaceRequest, |
| 545 | opts *createWorkspaceOptions, |
| 546 | ) (codersdk.Workspace, error) { |
| 547 | if opts == nil { |
| 548 | opts = &createWorkspaceOptions{} |
| 549 | } |
| 550 | |
| 551 | template, err := requestTemplate(ctx, req, api.Database) |
| 552 | if err != nil { |
| 553 | return codersdk.Workspace{}, err |
| 554 | } |
| 555 | |
| 556 | // This is a premature auth check to avoid doing unnecessary work if the user |
| 557 | // doesn't have permission to create a workspace. |
| 558 | if !api.HTTPAuth.AuthorizeContext(ctx, policy.ActionCreate, |
| 559 | rbac.ResourceWorkspace.InOrg(template.OrganizationID).WithOwner(owner.ID.String())) { |
| 560 | // If this check fails, return a proper unauthorized error to the user to indicate |
| 561 | // what is going on. |
| 562 | return codersdk.Workspace{}, httperror.NewResponseError(http.StatusForbidden, codersdk.Response{ |
| 563 | Message: "Unauthorized to create workspace.", |
| 564 | Detail: "You are unable to create a workspace in this organization. " + |
| 565 | "It is possible to have access to the template, but not be able to create a workspace. " + |
| 566 | "Please contact an administrator about your permissions if you feel this is an error.", |
| 567 | }) |
| 568 | } |
| 569 | |
| 570 | // Update audit log's organization |
| 571 | auditReq.UpdateOrganizationID(template.OrganizationID) |
| 572 | |
| 573 | // Do this upfront to save work. If this fails, the rest of the work |
| 574 | // would be wasted. |
| 575 | if !api.HTTPAuth.AuthorizeContext(ctx, policy.ActionCreate, |
| 576 | rbac.ResourceWorkspace.InOrg(template.OrganizationID).WithOwner(owner.ID.String())) { |
| 577 | return codersdk.Workspace{}, httperror.ErrResourceNotFound |
| 578 | } |
| 579 | // The user also needs permission to use the template. At this point they have |
| 580 | // read perms, but not necessarily "use". This is also checked in `db.InsertWorkspace`. |
| 581 | // Doing this up front can save some work below if the user doesn't have permission. |
| 582 | if !api.HTTPAuth.AuthorizeContext(ctx, policy.ActionUse, template) { |
| 583 | return codersdk.Workspace{}, httperror.NewResponseError(http.StatusForbidden, codersdk.Response{ |
| 584 | Message: fmt.Sprintf("Unauthorized access to use the template %q.", template.Name), |
| 585 | Detail: "Although you are able to view the template, you are unable to create a workspace using it. " + |
| 586 | "Please contact an administrator about your permissions if you feel this is an error.", |
| 587 | }) |
| 588 | } |
| 589 | |
| 590 | templateAccessControl := (*(api.AccessControlStore.Load())).GetTemplateAccessControl(template) |
| 591 | if templateAccessControl.IsDeprecated() { |
| 592 | return codersdk.Workspace{}, httperror.NewResponseError(http.StatusBadRequest, codersdk.Response{ |
| 593 | Message: fmt.Sprintf("Template %q has been deprecated, and cannot be used to create a new workspace.", template.Name), |
| 594 | // Pass the deprecated message to the user. |
| 595 | Detail: templateAccessControl.Deprecated, |