chore: Make deployment admin page show better durations (#6856)

* chore: Make deployment admin page show better durations

Also fix group mappings
This commit is contained in:
Steven Masley
2023-03-29 16:26:20 -05:00
committed by GitHub
parent 872037bf85
commit 90da09bc2c
8 changed files with 115 additions and 37 deletions

View File

@ -190,19 +190,6 @@ func (d *Duration) String() string {
return time.Duration(*d).String() return time.Duration(*d).String()
} }
func (d *Duration) MarshalJSON() ([]byte, error) {
return json.Marshal(d.String())
}
func (d *Duration) UnmarshalJSON(b []byte) error {
var s string
err := json.Unmarshal(b, &s)
if err != nil {
return err
}
return d.Set(s)
}
func (Duration) Type() string { func (Duration) Type() string {
return "duration" return "duration"
} }

View File

@ -170,7 +170,7 @@ backed by Tailscale and WireGuard.
--http-address string, $CODER_HTTP_ADDRESS (default: 127.0.0.1:3000) --http-address string, $CODER_HTTP_ADDRESS (default: 127.0.0.1:3000)
HTTP bind address of the server. Unset to disable the HTTP endpoint. HTTP bind address of the server. Unset to disable the HTTP endpoint.
--max-token-lifetime duration, $CODER_MAX_TOKEN_LIFETIME (default: 2562047h47m16.854775807s) --max-token-lifetime duration, $CODER_MAX_TOKEN_LIFETIME (default: 876600h0m0s)
The maximum lifetime duration users can specify when creating an API The maximum lifetime duration users can specify when creating an API
token. token.

View File

@ -6,7 +6,6 @@ import (
"database/sql" "database/sql"
"errors" "errors"
"fmt" "fmt"
"math"
"net" "net"
"net/http" "net/http"
"strconv" "strconv"
@ -354,19 +353,10 @@ func (api *API) tokenConfig(rw http.ResponseWriter, r *http.Request) {
return return
} }
var maxTokenLifetime time.Duration
// if --max-token-lifetime is unset (default value is math.MaxInt64)
// send back a falsy value
if values.MaxTokenLifetime.Value() == time.Duration(math.MaxInt64) {
maxTokenLifetime = 0
} else {
maxTokenLifetime = values.MaxTokenLifetime.Value()
}
httpapi.Write( httpapi.Write(
r.Context(), rw, http.StatusOK, r.Context(), rw, http.StatusOK,
codersdk.TokenConfig{ codersdk.TokenConfig{
MaxTokenLifetime: maxTokenLifetime, MaxTokenLifetime: values.MaxTokenLifetime.Value(),
}, },
) )
} }

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"flag" "flag"
"math"
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
@ -1113,7 +1112,10 @@ when required by your organization's security policy.`,
Description: "The maximum lifetime duration users can specify when creating an API token.", Description: "The maximum lifetime duration users can specify when creating an API token.",
Flag: "max-token-lifetime", Flag: "max-token-lifetime",
Env: "CODER_MAX_TOKEN_LIFETIME", Env: "CODER_MAX_TOKEN_LIFETIME",
Default: time.Duration(math.MaxInt64).String(), // The default value is essentially "forever", so just use 100 years.
// We have to add in the 25 leap days for the frontend to show the
// "100 years" correctly.
Default: ((100 * 365 * time.Hour * 24) + (25 * time.Hour * 24)).String(),
Value: &c.MaxTokenLifetime, Value: &c.MaxTokenLifetime,
Group: &deploymentGroupNetworkingHTTP, Group: &deploymentGroupNetworkingHTTP,
YAML: "maxTokenLifetime", YAML: "maxTokenLifetime",

View File

@ -233,7 +233,7 @@ Output Stackdriver compatible logs to a given file.
| ----------- | -------------------------------------- | | ----------- | -------------------------------------- |
| Type | <code>duration</code> | | Type | <code>duration</code> |
| Environment | <code>$CODER_MAX_TOKEN_LIFETIME</code> | | Environment | <code>$CODER_MAX_TOKEN_LIFETIME</code> |
| Default | <code>2562047h47m16.854775807s</code> | | Default | <code>876600h0m0s</code> |
The maximum lifetime duration users can specify when creating an API token. The maximum lifetime duration users can specify when creating an API token.

View File

@ -0,0 +1,73 @@
import { optionValue } from "./OptionsTable"
import { DeploymentOption } from "api/types"
const defaultOption: DeploymentOption = {
name: "",
description: "",
flag: "",
flag_shorthand: "",
value: "",
hidden: false,
}
describe("optionValue", () => {
it.each<{
option: DeploymentOption
expected: string | string[] | unknown
}>([
{
option: {
...defaultOption,
name: "Max Token Lifetime",
value: 3600 * 1e9,
},
expected: "1 hour",
},
{
option: {
...defaultOption,
name: "Max Token Lifetime",
value: 24 * 3600 * 1e9,
},
expected: "1 day",
},
{
option: {
...defaultOption,
name: "Session Duration",
value: 3600 * 1e9,
},
expected: "1 hour",
},
{
option: {
...defaultOption,
name: "Session Duration",
value: 24 * 3600 * 1e9,
},
expected: "1 day",
},
{
option: {
...defaultOption,
name: "Strict-Transport-Security",
value: 1000,
},
expected: "1000s",
},
{
option: {
...defaultOption,
name: "OIDC Group Mapping",
value: {
"123": "foo",
"456": "bar",
"789": "baz",
},
},
expected: [`"123"->"foo"`, `"456"->"bar"`, `"789"->"baz"`],
},
])(`[$option.name]optionValue($option.value)`, ({ option, expected }) => {
expect(optionValue(option)).toEqual(expected)
})
})

View File

@ -13,6 +13,7 @@ import {
} from "components/DeploySettingsLayout/Option" } from "components/DeploySettingsLayout/Option"
import { FC } from "react" import { FC } from "react"
import { DisabledBadge } from "./Badges" import { DisabledBadge } from "./Badges"
import { intervalToDuration, formatDuration } from "date-fns"
const OptionsTable: FC<{ const OptionsTable: FC<{
options: DeploymentOption[] options: DeploymentOption[]
@ -34,7 +35,11 @@ const OptionsTable: FC<{
</TableHead> </TableHead>
<TableBody> <TableBody>
{Object.values(options).map((option) => { {Object.values(options).map((option) => {
if (option.value === null || option.value === "") { if (
option.value === null ||
option.value === "" ||
option.value === undefined
) {
return null return null
} }
return ( return (
@ -45,7 +50,7 @@ const OptionsTable: FC<{
</TableCell> </TableCell>
<TableCell> <TableCell>
<OptionValue>{option.value}</OptionValue> <OptionValue>{optionValue(option)}</OptionValue>
</TableCell> </TableCell>
</TableRow> </TableRow>
) )
@ -56,6 +61,31 @@ const OptionsTable: FC<{
) )
} }
// optionValue is a helper function to format the value of a specific deployment options
export function optionValue(
option: DeploymentOption,
): string[] | string | unknown {
switch (option.name) {
case "Max Token Lifetime":
case "Session Duration":
// intervalToDuration takes ms, so convert nanoseconds to ms
return formatDuration(
intervalToDuration({ start: 0, end: (option.value as number) / 1e6 }),
)
case "Strict-Transport-Security":
if (option.value === 0) {
return "Disabled"
}
return (option.value as number).toString() + "s"
case "OIDC Group Mapping":
return Object.entries(option.value as Record<string, string>).map(
([key, value]) => `"${key}"->"${value}"`,
)
default:
return option.value
}
}
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
table: { table: {
"& td": { "& td": {

View File

@ -12,10 +12,6 @@ describe("unit/CreateTokenForm", () => {
maxTokenLifetime: number maxTokenLifetime: number
expected: LifetimeDay[] expected: LifetimeDay[]
}>([ }>([
{
maxTokenLifetime: 0,
expected: lifetimeDayPresets,
},
{ maxTokenLifetime: 6 * 24 * NANO_HOUR, expected: [] }, { maxTokenLifetime: 6 * 24 * NANO_HOUR, expected: [] },
{ {
maxTokenLifetime: 20 * 24 * NANO_HOUR, maxTokenLifetime: 20 * 24 * NANO_HOUR,