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
10 changes: 10 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,13 @@ GIN_MODE=release
AUTH_CLIENT_ID=
# ex. "https://login.microsoftonline.com/<tenant-id>/v2.0 for Azure Entra -- used for discovery
AUTH_ISSUER=
# DOWNLOAD RPC PACKAGING
# GitHub repository the rpc-go releases are fetched from.
RPC_REPO=device-management-toolkit/rpc-go
# Ceiling for the auth-token lifetime a Download RPC package may request.
# Requests above it are rejected; 0 or unset uses 24h.
RPC_MAX_TOKEN_TTL=24h
# Directory of cached rpc-go builds, laid out as <dir>/<version>/<asset files>.
RPC_LOCAL_DIR=
# true lists and serves builds from RPC_LOCAL_DIR only; false fetches from GitHub first, then RPC_LOCAL_DIR.
RPC_DISABLE_FETCH=false
25 changes: 25 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ var TrayMode bool
var (
ErrJWTExpirationInvalid = errors.New("config: auth.jwtExpiration must be at least 1 minute (e.g. 24h) — very short expirations render tokens unusable")
ErrRedirectionJWTExpirationInvalid = errors.New("config: auth.redirectionJWTExpiration must be at least 1 minute (e.g. 5m) — very short expirations render redirection tokens unusable")
ErrMaxTokenTTLInvalid = errors.New("config: package.max_token_ttl must be at least 1 minute (e.g. 24h), or 0 to use the default")
ErrLocalDirRequired = errors.New("config: package.local_dir is required when package.disable_fetch is true — set RPC_LOCAL_DIR or local_dir in config.yml")
ErrJWTKeyMissing = errors.New("config: auth.jwtKey is required — set AUTH_JWT_KEY environment variable or jwtKey in config.yml to a strong secret")
)

Expand Down Expand Up @@ -54,6 +56,7 @@ type (
EA `yaml:"ea"`
Auth `yaml:"auth"`
UI `yaml:"ui"`
Package `yaml:"package"`
}

// App -.
Expand Down Expand Up @@ -152,6 +155,16 @@ type (
UI struct {
ExternalURL string `yaml:"externalUrl" env:"UI_EXTERNAL_URL"`
}

// Package -. Settings for the Download RPC packaging endpoints.
Package struct {
RPCRepo string `yaml:"rpc_repo" env:"RPC_REPO"`
LocalDir string `yaml:"local_dir" env:"RPC_LOCAL_DIR"`
// DisableFetch serves rpc-go builds from LocalDir only, never contacting GitHub.
DisableFetch bool `yaml:"disable_fetch" env:"RPC_DISABLE_FETCH"`
// MaxTokenTTL caps the auth-token lifetime a package request may ask for.
MaxTokenTTL time.Duration `yaml:"max_token_ttl" env:"RPC_MAX_TOKEN_TTL"`
}
)

// CookieAuthEnabled reports whether the HttpOnly session cookie is in use. Off
Expand Down Expand Up @@ -251,6 +264,10 @@ func defaultConfig() *Config {
UI: UI{
ExternalURL: "",
},
Package: Package{
RPCRepo: "device-management-toolkit/rpc-go",
MaxTokenTTL: 24 * time.Hour,
},
}
}

Expand Down Expand Up @@ -471,6 +488,14 @@ func (c *Config) validate() error {
return ErrRedirectionJWTExpirationInvalid
}

if c.MaxTokenTTL != 0 && c.MaxTokenTTL < time.Minute {
return ErrMaxTokenTTLInvalid
}

if c.DisableFetch && c.LocalDir == "" {
return ErrLocalDirRequired
}

if !c.Disabled && c.JWTKey == "" {
return ErrJWTKeyMissing
}
Expand Down
3 changes: 3 additions & 0 deletions config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,7 @@ ui:
# - Ignored: When building without 'noui' tag (embedded UI is served normally)
# Example: https://ui.example.com
externalUrl: ""
package:
rpc_repo: device-management-toolkit/rpc-go
local_dir: ""

44 changes: 44 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,50 @@ func TestValidate_SubMinuteJWTExpiration(t *testing.T) {
require.ErrorIs(t, err, ErrJWTExpirationInvalid)
}

func TestValidate_SubMinuteMaxTokenTTL(t *testing.T) {
t.Parallel()

cfg := defaultConfig()
cfg.MaxTokenTTL = 30 * time.Second

err := cfg.validate()
require.ErrorIs(t, err, ErrMaxTokenTTLInvalid)
}

func TestValidate_NegativeMaxTokenTTL(t *testing.T) {
t.Parallel()

cfg := defaultConfig()
cfg.MaxTokenTTL = -1 * time.Hour

err := cfg.validate()
require.ErrorIs(t, err, ErrMaxTokenTTLInvalid)
}

func TestValidate_UnsetMaxTokenTTLIsAllowed(t *testing.T) {
t.Parallel()

cfg := defaultConfig()
cfg.MaxTokenTTL = 0
cfg.JWTKey = "test-jwt-key"

require.NoError(t, cfg.validate())
}

func TestValidate_DisableFetchRequiresLocalDir(t *testing.T) {
t.Parallel()

cfg := defaultConfig()
cfg.JWTKey = "test-jwt-key"
cfg.DisableFetch = true

require.ErrorIs(t, cfg.validate(), ErrLocalDirRequired)

cfg.LocalDir = "/opt/rpc-go"

require.NoError(t, cfg.validate())
}

func TestValidate_ZeroRedirectionJWTExpiration(t *testing.T) {
t.Parallel()

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ require (
github.com/zsais/go-gin-prometheus v1.0.3
go.mongodb.org/mongo-driver/v2 v2.9.1
go.uber.org/mock v0.6.0
golang.org/x/mod v0.40.0
golang.org/x/sys v0.48.0
gopkg.in/yaml.v2 v2.4.0
modernc.org/sqlite v1.59.0
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -298,8 +298,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y=
golang.org/x/crypto v0.56.0/go.mod h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk=
golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40=
golang.org/x/mod v0.40.0 h1:hUv+3cXcdRHz08UmSiOob7sadHig73uo5bkXxQ/tvUs=
golang.org/x/mod v0.40.0/go.mod h1:0/weTWkPWGBikyTWAX3dkjVztMmBA5hM0DH6BElSupE=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
Expand Down Expand Up @@ -331,8 +331,8 @@ golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE=
golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk=
golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI=
golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
Expand Down
Loading
Loading