mirror of
https://github.com/coder/coder.git
synced 2025-07-13 21:36:50 +00:00
chore: remove middleware to request version and entitlement warnings (#12750)
This cleans up `root.go` a bit, adds tests for middleware HTTP transport functions, and removes two HTTP requests we always always performed previously when executing *any* client command. It should improve CLI performance (especially for users with higher latency).
This commit is contained in:
@ -94,17 +94,18 @@ func New(ctx context.Context, options *Options) (_ *API, err error) {
|
||||
return nil, xerrors.Errorf("init database encryption: %w", err)
|
||||
}
|
||||
options.Database = cryptDB
|
||||
|
||||
api := &API{
|
||||
ctx: ctx,
|
||||
cancel: cancelFunc,
|
||||
AGPL: coderd.New(options.Options),
|
||||
Options: options,
|
||||
provisionerDaemonAuth: &provisionerDaemonAuth{
|
||||
psk: options.ProvisionerDaemonPSK,
|
||||
authorizer: options.Authorizer,
|
||||
},
|
||||
}
|
||||
// This must happen before coderd initialization!
|
||||
options.PostAuthAdditionalHeadersFunc = api.writeEntitlementWarningsHeader
|
||||
api.AGPL = coderd.New(options.Options)
|
||||
defer func() {
|
||||
if err != nil {
|
||||
_ = api.Close()
|
||||
@ -144,29 +145,32 @@ func New(ctx context.Context, options *Options) (_ *API, err error) {
|
||||
OIDC: options.OIDCConfig,
|
||||
}
|
||||
apiKeyMiddleware := httpmw.ExtractAPIKeyMW(httpmw.ExtractAPIKeyConfig{
|
||||
DB: options.Database,
|
||||
OAuth2Configs: oauthConfigs,
|
||||
RedirectToLogin: false,
|
||||
DisableSessionExpiryRefresh: options.DeploymentValues.DisableSessionExpiryRefresh.Value(),
|
||||
Optional: false,
|
||||
SessionTokenFunc: nil, // Default behavior
|
||||
DB: options.Database,
|
||||
OAuth2Configs: oauthConfigs,
|
||||
RedirectToLogin: false,
|
||||
DisableSessionExpiryRefresh: options.DeploymentValues.DisableSessionExpiryRefresh.Value(),
|
||||
Optional: false,
|
||||
SessionTokenFunc: nil, // Default behavior
|
||||
PostAuthAdditionalHeadersFunc: options.PostAuthAdditionalHeadersFunc,
|
||||
})
|
||||
// Same as above but it redirects to the login page.
|
||||
apiKeyMiddlewareRedirect := httpmw.ExtractAPIKeyMW(httpmw.ExtractAPIKeyConfig{
|
||||
DB: options.Database,
|
||||
OAuth2Configs: oauthConfigs,
|
||||
RedirectToLogin: true,
|
||||
DisableSessionExpiryRefresh: options.DeploymentValues.DisableSessionExpiryRefresh.Value(),
|
||||
Optional: false,
|
||||
SessionTokenFunc: nil, // Default behavior
|
||||
DB: options.Database,
|
||||
OAuth2Configs: oauthConfigs,
|
||||
RedirectToLogin: true,
|
||||
DisableSessionExpiryRefresh: options.DeploymentValues.DisableSessionExpiryRefresh.Value(),
|
||||
Optional: false,
|
||||
SessionTokenFunc: nil, // Default behavior
|
||||
PostAuthAdditionalHeadersFunc: options.PostAuthAdditionalHeadersFunc,
|
||||
})
|
||||
apiKeyMiddlewareOptional := httpmw.ExtractAPIKeyMW(httpmw.ExtractAPIKeyConfig{
|
||||
DB: options.Database,
|
||||
OAuth2Configs: oauthConfigs,
|
||||
RedirectToLogin: false,
|
||||
DisableSessionExpiryRefresh: options.DeploymentValues.DisableSessionExpiryRefresh.Value(),
|
||||
Optional: true,
|
||||
SessionTokenFunc: nil, // Default behavior
|
||||
DB: options.Database,
|
||||
OAuth2Configs: oauthConfigs,
|
||||
RedirectToLogin: false,
|
||||
DisableSessionExpiryRefresh: options.DeploymentValues.DisableSessionExpiryRefresh.Value(),
|
||||
Optional: true,
|
||||
SessionTokenFunc: nil, // Default behavior
|
||||
PostAuthAdditionalHeadersFunc: options.PostAuthAdditionalHeadersFunc,
|
||||
})
|
||||
|
||||
deploymentID, err := options.Database.GetDeploymentID(ctx)
|
||||
@ -531,6 +535,38 @@ type API struct {
|
||||
tailnetService *tailnet.ClientService
|
||||
}
|
||||
|
||||
// writeEntitlementWarningsHeader writes the entitlement warnings to the response header
|
||||
// for all authenticated users with roles. If there are no warnings, this header will not be written.
|
||||
//
|
||||
// This header is used by the CLI to display warnings to the user without having
|
||||
// to make additional requests!
|
||||
func (api *API) writeEntitlementWarningsHeader(a httpmw.Authorization, header http.Header) {
|
||||
roles, err := a.Actor.Roles.Expand()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
nonMemberRoles := 0
|
||||
for _, role := range roles {
|
||||
// The member role is implied, and not assignable.
|
||||
// If there is no display name, then the role is also unassigned.
|
||||
// This is not the ideal logic, but works for now.
|
||||
if role.Name == rbac.RoleMember() || (role.DisplayName == "") {
|
||||
continue
|
||||
}
|
||||
nonMemberRoles++
|
||||
}
|
||||
if nonMemberRoles == 0 {
|
||||
// Don't show entitlement warnings if the user
|
||||
// has no roles. This is a normal user!
|
||||
return
|
||||
}
|
||||
api.entitlementsMu.RLock()
|
||||
defer api.entitlementsMu.RUnlock()
|
||||
for _, warning := range api.entitlements.Warnings {
|
||||
header.Add(codersdk.EntitlementsWarningHeader, warning)
|
||||
}
|
||||
}
|
||||
|
||||
func (api *API) Close() error {
|
||||
// Replica manager should be closed first. This is because the replica
|
||||
// manager updates the replica's table in the database when it closes.
|
||||
|
@ -3,6 +3,7 @@ package coderd_test
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
@ -197,6 +198,40 @@ func TestEntitlements(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestEntitlements_HeaderWarnings(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("ExistForAdmin", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
adminClient, _ := coderdenttest.New(t, &coderdenttest.Options{
|
||||
AuditLogging: true,
|
||||
LicenseOptions: &coderdenttest.LicenseOptions{
|
||||
AllFeatures: false,
|
||||
},
|
||||
})
|
||||
//nolint:gocritic // This isn't actually bypassing any RBAC checks
|
||||
res, err := adminClient.Request(context.Background(), http.MethodGet, "/api/v2/users/me", nil)
|
||||
require.NoError(t, err)
|
||||
defer res.Body.Close()
|
||||
require.Equal(t, http.StatusOK, res.StatusCode)
|
||||
require.NotEmpty(t, res.Header.Values(codersdk.EntitlementsWarningHeader))
|
||||
})
|
||||
t.Run("NoneForNormalUser", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
adminClient, adminUser := coderdenttest.New(t, &coderdenttest.Options{
|
||||
AuditLogging: true,
|
||||
LicenseOptions: &coderdenttest.LicenseOptions{
|
||||
AllFeatures: false,
|
||||
},
|
||||
})
|
||||
anotherClient, _ := coderdtest.CreateAnotherUser(t, adminClient, adminUser.OrganizationID)
|
||||
res, err := anotherClient.Request(context.Background(), http.MethodGet, "/api/v2/users/me", nil)
|
||||
require.NoError(t, err)
|
||||
defer res.Body.Close()
|
||||
require.Equal(t, http.StatusOK, res.StatusCode)
|
||||
require.Empty(t, res.Header.Values(codersdk.EntitlementsWarningHeader))
|
||||
})
|
||||
}
|
||||
|
||||
func TestAuditLogging(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("Enabled", func(t *testing.T) {
|
||||
|
Reference in New Issue
Block a user