Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion coderd/audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"cdr.dev/slog"
"github.com/coder/coder/coderd/audit"
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/coderd/database/db2sdk"
"github.com/coder/coder/coderd/httpapi"
"github.com/coder/coder/coderd/httpmw"
"github.com/coder/coder/coderd/rbac"
Expand Down Expand Up @@ -193,7 +194,7 @@ func (api *API) convertAuditLog(ctx context.Context, dblog database.GetAuditLogs

for _, roleName := range dblog.UserRoles {
rbacRole, _ := rbac.RoleByName(roleName)
user.Roles = append(user.Roles, convertRole(rbacRole))
user.Roles = append(user.Roles, db2sdk.Role(rbacRole))
}
}

Expand Down
23 changes: 15 additions & 8 deletions coderd/coderd.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,11 +293,13 @@ func New(options *Options) *API {
},
)

staticHandler := site.Handler(site.FS(), binFS, binHashes)
// Static file handler must be wrapped with HSTS handler if the
// StrictTransportSecurityAge is set. We only need to set this header on
// static files since it only affects browsers.
staticHandler = httpmw.HSTS(staticHandler, options.StrictTransportSecurityCfg)
staticHandler := site.New(&site.Options{
BinFS: binFS,
BinHashes: binHashes,
Database: options.Database,
SiteFS: site.FS(),
})
staticHandler.Experiments.Store(&experiments)

oauthConfigs := &httpmw.OAuth2Configs{
Github: options.GithubOAuth2Config,
Expand All @@ -313,7 +315,7 @@ func New(options *Options) *API {
ID: uuid.New(),
Options: options,
RootHandler: r,
siteHandler: staticHandler,
SiteHandler: staticHandler,
HTTPAuth: &HTTPAuthorizer{
Authorizer: options.Authorizer,
Logger: options.Logger,
Expand Down Expand Up @@ -813,7 +815,11 @@ func New(options *Options) *API {
// By default we do not add extra websocket connections to the CSP
return []string{}
})
r.NotFound(cspMW(compressHandler(http.HandlerFunc(api.siteHandler.ServeHTTP))).ServeHTTP)

// Static file handler must be wrapped with HSTS handler if the
// StrictTransportSecurityAge is set. We only need to set this header on
// static files since it only affects browsers.
r.NotFound(cspMW(compressHandler(httpmw.HSTS(api.SiteHandler, options.StrictTransportSecurityCfg))).ServeHTTP)

// This must be before all middleware to improve the response time.
// So make a new router, and mount the old one as the root.
Expand Down Expand Up @@ -858,7 +864,8 @@ type API struct {
// RootHandler serves "/"
RootHandler chi.Router

siteHandler http.Handler
// SiteHandler serves static files for the dashboard.
SiteHandler *site.Handler

WebsocketWaitMutex sync.Mutex
WebsocketWaitGroup sync.WaitGroup
Expand Down
31 changes: 31 additions & 0 deletions coderd/database/db2sdk/db2sdk.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import (
"encoding/json"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good new package

"time"

"github.com/google/uuid"

"github.com/coder/coder/coderd/database"
"github.com/coder/coder/coderd/parameter"
"github.com/coder/coder/coderd/rbac"
"github.com/coder/coder/codersdk"
"github.com/coder/coder/provisionersdk/proto"
)
Expand Down Expand Up @@ -100,3 +103,31 @@ func ProvisionerJobStatus(provisionerJob database.ProvisionerJob) codersdk.Provi
return codersdk.ProvisionerJobRunning
}
}

func User(user database.User, organizationIDs []uuid.UUID) codersdk.User {
convertedUser := codersdk.User{
ID: user.ID,
Email: user.Email,
CreatedAt: user.CreatedAt,
LastSeenAt: user.LastSeenAt,
Username: user.Username,
Status: codersdk.UserStatus(user.Status),
OrganizationIDs: organizationIDs,
Roles: make([]codersdk.Role, 0, len(user.RBACRoles)),
AvatarURL: user.AvatarURL.String,
}

for _, roleName := range user.RBACRoles {
rbacRole, _ := rbac.RoleByName(roleName)
convertedUser.Roles = append(convertedUser.Roles, Role(rbacRole))
}

return convertedUser
}

func Role(role rbac.Role) codersdk.Role {
return codersdk.Role{
DisplayName: role.DisplayName,
Name: role.Name,
}
}
3 changes: 2 additions & 1 deletion coderd/members.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"golang.org/x/xerrors"

"github.com/coder/coder/coderd/database/db2sdk"
"github.com/coder/coder/coderd/rbac"

"github.com/coder/coder/coderd/database"
Expand Down Expand Up @@ -104,7 +105,7 @@ func convertOrganizationMember(mem database.OrganizationMember) codersdk.Organiz

for _, roleName := range mem.Roles {
rbacRole, _ := rbac.RoleByName(roleName)
convertedMember.Roles = append(convertedMember.Roles, convertRole(rbacRole))
convertedMember.Roles = append(convertedMember.Roles, db2sdk.Role(rbacRole))
}
return convertedMember
}
7 changes: 0 additions & 7 deletions coderd/roles.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,6 @@ func (api *API) assignableOrgRoles(rw http.ResponseWriter, r *http.Request) {
httpapi.Write(ctx, rw, http.StatusOK, assignableRoles(actorRoles.Actor.Roles, roles))
}

func convertRole(role rbac.Role) codersdk.Role {
return codersdk.Role{
DisplayName: role.DisplayName,
Name: role.Name,
}
}

func assignableRoles(actorRoles rbac.ExpandableRoles, roles []rbac.Role) []codersdk.AssignableRoles {
assignable := make([]codersdk.AssignableRoles, 0)
for _, role := range roles {
Expand Down
34 changes: 7 additions & 27 deletions coderd/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (

"github.com/coder/coder/coderd/audit"
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/coderd/database/db2sdk"
"github.com/coder/coder/coderd/database/dbauthz"
"github.com/coder/coder/coderd/gitsshkey"
"github.com/coder/coder/coderd/httpapi"
Expand Down Expand Up @@ -401,7 +402,7 @@ func (api *API) postUser(rw http.ResponseWriter, r *http.Request) {
Users: []telemetry.User{telemetry.ConvertUser(user)},
})

httpapi.Write(ctx, rw, http.StatusCreated, convertUser(user, []uuid.UUID{req.OrganizationID}))
httpapi.Write(ctx, rw, http.StatusCreated, db2sdk.User(user, []uuid.UUID{req.OrganizationID}))
}

// @Summary Delete user
Expand Down Expand Up @@ -495,7 +496,7 @@ func (api *API) userByName(rw http.ResponseWriter, r *http.Request) {
return
}

httpapi.Write(ctx, rw, http.StatusOK, convertUser(user, organizationIDs))
httpapi.Write(ctx, rw, http.StatusOK, db2sdk.User(user, organizationIDs))
}

// @Summary Update user profile
Expand Down Expand Up @@ -580,7 +581,7 @@ func (api *API) putUserProfile(rw http.ResponseWriter, r *http.Request) {
return
}

httpapi.Write(ctx, rw, http.StatusOK, convertUser(updatedUserProfile, organizationIDs))
httpapi.Write(ctx, rw, http.StatusOK, db2sdk.User(updatedUserProfile, organizationIDs))
}

// @Summary Suspend user account
Expand Down Expand Up @@ -667,7 +668,7 @@ func (api *API) putUserStatus(status database.UserStatus) func(rw http.ResponseW
return
}

httpapi.Write(ctx, rw, http.StatusOK, convertUser(suspendedUser, organizations))
httpapi.Write(ctx, rw, http.StatusOK, db2sdk.User(suspendedUser, organizations))
}
}

Expand Down Expand Up @@ -892,7 +893,7 @@ func (api *API) putUserRoles(rw http.ResponseWriter, r *http.Request) {
return
}

httpapi.Write(ctx, rw, http.StatusOK, convertUser(updatedUser, organizationIDs))
httpapi.Write(ctx, rw, http.StatusOK, db2sdk.User(updatedUser, organizationIDs))
}

// updateSiteUserRoles will ensure only site wide roles are passed in as arguments.
Expand Down Expand Up @@ -1087,32 +1088,11 @@ func (api *API) CreateUser(ctx context.Context, store database.Store, req Create
}, nil)
}

func convertUser(user database.User, organizationIDs []uuid.UUID) codersdk.User {
convertedUser := codersdk.User{
ID: user.ID,
Email: user.Email,
CreatedAt: user.CreatedAt,
LastSeenAt: user.LastSeenAt,
Username: user.Username,
Status: codersdk.UserStatus(user.Status),
OrganizationIDs: organizationIDs,
Roles: make([]codersdk.Role, 0, len(user.RBACRoles)),
AvatarURL: user.AvatarURL.String,
}

for _, roleName := range user.RBACRoles {
rbacRole, _ := rbac.RoleByName(roleName)
convertedUser.Roles = append(convertedUser.Roles, convertRole(rbacRole))
}

return convertedUser
}

func convertUsers(users []database.User, organizationIDsByUserID map[uuid.UUID][]uuid.UUID) []codersdk.User {
converted := make([]codersdk.User, 0, len(users))
for _, u := range users {
userOrganizationIDs := organizationIDsByUserID[u.ID]
converted = append(converted, convertUser(u, userOrganizationIDs))
converted = append(converted, db2sdk.User(u, userOrganizationIDs))
}
return converted
}
Expand Down
69 changes: 41 additions & 28 deletions enterprise/coderd/appearance.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package coderd

import (
"context"
"database/sql"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"net/http"

"golang.org/x/sync/errgroup"
"golang.org/x/xerrors"

"github.com/coder/coder/coderd/httpapi"
Expand Down Expand Up @@ -41,35 +43,49 @@ var DefaultSupportLinks = []codersdk.LinkConfig{
// @Success 200 {object} codersdk.AppearanceConfig
// @Router /appearance [get]
func (api *API) appearance(rw http.ResponseWriter, r *http.Request) {
cfg, err := api.fetchAppearanceConfig(r.Context())
if err != nil {
httpapi.Write(r.Context(), rw, http.StatusInternalServerError, codersdk.Response{
Message: "Failed to fetch appearance config.",
Detail: err.Error(),
})
return
}

httpapi.Write(r.Context(), rw, http.StatusOK, cfg)
}

func (api *API) fetchAppearanceConfig(ctx context.Context) (codersdk.AppearanceConfig, error) {
api.entitlementsMu.RLock()
isEntitled := api.entitlements.Features[codersdk.FeatureAppearance].Entitlement == codersdk.EntitlementEntitled
api.entitlementsMu.RUnlock()

ctx := r.Context()

if !isEntitled {
httpapi.Write(ctx, rw, http.StatusOK, codersdk.AppearanceConfig{
return codersdk.AppearanceConfig{
SupportLinks: DefaultSupportLinks,
})
return
}, nil
}

logoURL, err := api.Database.GetLogoURL(ctx)
if err != nil && !errors.Is(err, sql.ErrNoRows) {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Failed to fetch logo URL.",
Detail: err.Error(),
})
return
}

serviceBannerJSON, err := api.Database.GetServiceBanner(r.Context())
if err != nil && !errors.Is(err, sql.ErrNoRows) {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Failed to fetch service banner.",
Detail: err.Error(),
})
return
var eg errgroup.Group
var logoURL string
var serviceBannerJSON string
eg.Go(func() (err error) {
logoURL, err = api.Database.GetLogoURL(ctx)
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return xerrors.Errorf("get logo url: %w", err)
}
return nil
})
eg.Go(func() (err error) {
serviceBannerJSON, err = api.Database.GetServiceBanner(ctx)
if err != nil && !errors.Is(err, sql.ErrNoRows) {
return xerrors.Errorf("get service banner: %w", err)
}
return nil
})
err := eg.Wait()
if err != nil {
return codersdk.AppearanceConfig{}, err
}

cfg := codersdk.AppearanceConfig{
Expand All @@ -78,12 +94,9 @@ func (api *API) appearance(rw http.ResponseWriter, r *http.Request) {
if serviceBannerJSON != "" {
err = json.Unmarshal([]byte(serviceBannerJSON), &cfg.ServiceBanner)
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: fmt.Sprintf(
"unmarshal json: %+v, raw: %s", err, serviceBannerJSON,
),
})
return
return codersdk.AppearanceConfig{}, xerrors.Errorf(
"unmarshal json: %w, raw: %s", err, serviceBannerJSON,
)
}
}

Expand All @@ -93,7 +106,7 @@ func (api *API) appearance(rw http.ResponseWriter, r *http.Request) {
cfg.SupportLinks = api.DeploymentValues.Support.Links.Value
}

httpapi.Write(r.Context(), rw, http.StatusOK, cfg)
return cfg, nil
}

func validateHexColor(color string) error {
Expand Down
2 changes: 2 additions & 0 deletions enterprise/coderd/coderd.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ func New(ctx context.Context, options *Options) (_ *API, err error) {
}()

api.AGPL.Options.SetUserGroups = api.setUserGroups
api.AGPL.SiteHandler.AppearanceFetcher = api.fetchAppearanceConfig

oauthConfigs := &httpmw.OAuth2Configs{
Github: options.GithubOAuth2Config,
Expand Down Expand Up @@ -451,6 +452,7 @@ func (api *API) updateEntitlements(ctx context.Context) error {
}

api.entitlements = entitlements
api.AGPL.SiteHandler.Entitlements.Store(&entitlements)

return nil
}
Expand Down
7 changes: 5 additions & 2 deletions site/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#17172E" />
<meta name="application-name" content="Coder2" />
<meta name="application-name" content="Coder" />
<meta property="og:type" content="website" />
<meta property="csp-nonce" content="{{ .CSP.Nonce }}" />
<meta property="csrf-token" content="{{ .CSRF.Token }}" />
<meta property="build-info" content="{{ .BuildInfo }}" />
<meta property="user" content="{{ .User }}" />
<meta property="entitlements" content="{{ .Entitlements }}" />
<meta property="appearance" content="{{ .Appearance }}" />
<meta property="experiments" content="{{ .Experiments }}" />
<!-- We need to set data-react-helmet to be able to override it in the workspace page -->
<link
rel="alternate icon"
Expand Down
Loading