Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit190cd1c

Browse files
stirbydannykoppingspikecurtis
authored
chore: apply fixes for the 2.15 release (#14540)
* Minor fixups, added troubleshooting (#14519)(cherry picked from commit66c8060)* fix: allow posting licenses that will be valid in future (#14491)(cherry picked from commit5bd5801)* fix: stop reporting future licenses as errors (#14492)(cherry picked from commit4eac2ac)---------Co-authored-by: Danny Kopping <danny@coder.com>Co-authored-by: Spike Curtis <spike@coder.com>
1 parent0ef8514 commit190cd1c

File tree

7 files changed

+156
-30
lines changed

7 files changed

+156
-30
lines changed

‎docs/admin/notifications.md

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,9 @@ can only be delivered to one method, and this method is configured globally with
7777
[`CODER_NOTIFICATIONS_METHOD`](https://coder.com/docs/reference/cli/server#--notifications-method)
7878
(default:`smtp`).
7979

80-
Enterprise customers canconfigured which method to use for each of the
81-
supported[Events](#events); see the[Preferences](#preferences) section below
82-
for moredetails.
80+
Enterprise customers canconfigure which method to use for each of the supported
81+
[Events](#events); see the[Preferences](#preferences) section below for more
82+
details.
8383

8484
##SMTP (Email)
8585

@@ -93,7 +93,7 @@ existing one.
9393
| :------:| ---------------------------------| -------------------------------------| -----------| -----------------------------------------| -------------|
9494
| ✔️|`--notifications-email-from`|`CODER_NOTIFICATIONS_EMAIL_FROM`|`string`| The sender's address to use.||
9595
| ✔️|`--notifications-email-smarthost`|`CODER_NOTIFICATIONS_EMAIL_SMARTHOST`|`host:port`| The SMTP relay to send messages through.| localhost:587|
96-
|-|`--notifications-email-hello`|`CODER_NOTIFICATIONS_EMAIL_HELLO`|`string`| The hostname identifying the SMTP server.| localhost|
96+
||`--notifications-email-hello`|`CODER_NOTIFICATIONS_EMAIL_HELLO`|`string`| The hostname identifying the SMTP server.| localhost|
9797

9898
**Authentication Settings:**
9999

@@ -252,6 +252,18 @@ To pause sending notifications, execute
252252
To resume sending notifications, execute
253253
[`coder notifications resume`](https://coder.com/docs/reference/cli/notifications_resume).
254254

255+
##Troubleshooting
256+
257+
If notifications are not being delivered, use the following methods to
258+
troubleshoot:
259+
260+
1. Ensure notifications are being added to the`notification_messages` table
261+
2. Review any error messages in the`status_reason` column, should an error have
262+
occurred
263+
3. Review the logs (search for the term`notifications`) for diagnostic
264+
information<br>_If you do not see any relevant logs, set
265+
`CODER_VERBOSE=true` or`--verbose` to output debug logs_
266+
255267
##Internals
256268

257269
The notification system is built to operate concurrently in a single- or
@@ -288,5 +300,4 @@ messages._
288300
- after`CODER_NOTIFICATIONS_MAX_SEND_ATTEMPTS` is exceeded, it transitions to
289301
`permanent_failure`
290302

291-
Diagnostic messages will be saved in the`notification_messages` table and will
292-
be logged, in the case of failure.
303+
See[Troubleshooting](#troubleshooting) above for more details.
35 KB
Loading

‎enterprise/coderd/coderdenttest/coderdenttest.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,10 @@ type LicenseOptions struct {
174174
// ExpiresAt is the time at which the license will hard expire.
175175
// ExpiresAt should always be greater then GraceAt.
176176
ExpiresAt time.Time
177+
// NotBefore is the time at which the license becomes valid. If set to the
178+
// zero value, the `nbf` claim on the license is set to 1 minute in the
179+
// past.
180+
NotBefore time.Time
177181
Features license.Features
178182
}
179183

@@ -195,6 +199,13 @@ func (opts *LicenseOptions) Valid(now time.Time) *LicenseOptions {
195199
returnopts
196200
}
197201

202+
func (opts*LicenseOptions)FutureTerm(now time.Time)*LicenseOptions {
203+
opts.NotBefore=now.Add(time.Hour*24)
204+
opts.ExpiresAt=now.Add(time.Hour*24*60)
205+
opts.GraceAt=now.Add(time.Hour*24*53)
206+
returnopts
207+
}
208+
198209
func (opts*LicenseOptions)UserLimit(limitint64)*LicenseOptions {
199210
returnopts.Feature(codersdk.FeatureUserLimit,limit)
200211
}
@@ -233,13 +244,16 @@ func GenerateLicense(t *testing.T, options LicenseOptions) string {
233244
ifoptions.GraceAt.IsZero() {
234245
options.GraceAt=time.Now().Add(time.Hour)
235246
}
247+
ifoptions.NotBefore.IsZero() {
248+
options.NotBefore=time.Now().Add(-time.Minute)
249+
}
236250

237251
c:=&license.Claims{
238252
RegisteredClaims: jwt.RegisteredClaims{
239253
ID:uuid.NewString(),
240254
Issuer:"test@testing.test",
241255
ExpiresAt:jwt.NewNumericDate(options.ExpiresAt),
242-
NotBefore:jwt.NewNumericDate(time.Now().Add(-time.Minute)),
256+
NotBefore:jwt.NewNumericDate(options.NotBefore),
243257
IssuedAt:jwt.NewNumericDate(time.Now().Add(-time.Minute)),
244258
},
245259
LicenseExpires:jwt.NewNumericDate(options.GraceAt),

‎enterprise/coderd/license/license.go

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,13 @@ func LicensesEntitlements(
100100
// 'Entitlements' group as a whole.
101101
for_,license:=rangelicenses {
102102
claims,err:=ParseClaims(license.JWT,keys)
103+
varvErr*jwt.ValidationError
104+
ifxerrors.As(err,&vErr)&&vErr.Is(jwt.ErrTokenNotValidYet) {
105+
// The license isn't valid yet. We don't consider any entitlements contained in it, but
106+
// it's also not an error. Just skip it silently. This can happen if an administrator
107+
// uploads a license for a new term that hasn't started yet.
108+
continue
109+
}
103110
iferr!=nil {
104111
entitlements.Errors=append(entitlements.Errors,
105112
fmt.Sprintf("Invalid license (%s) parsing claims: %s",license.UUID.String(),err.Error()))
@@ -287,6 +294,8 @@ var (
287294
ErrInvalidVersion=xerrors.New("license must be version 3")
288295
ErrMissingKeyID=xerrors.Errorf("JOSE header must contain %s",HeaderKeyID)
289296
ErrMissingLicenseExpires=xerrors.New("license missing license_expires")
297+
ErrMissingExp=xerrors.New("exp claim missing or not parsable")
298+
ErrMultipleIssues=xerrors.New("license has multiple issues; contact support")
290299
)
291300

292301
typeFeaturesmap[codersdk.FeatureName]int64
@@ -336,7 +345,7 @@ func ParseRaw(l string, keys map[string]ed25519.PublicKey) (jwt.MapClaims, error
336345
returnnil,xerrors.New("unable to parse Claims")
337346
}
338347

339-
// ParseClaims validates adatabase.License record, and if valid, returns the claims. If
348+
// ParseClaims validates araw JWT, and if valid, returns the claims. If
340349
// unparsable or invalid, it returns an error
341350
funcParseClaims(rawJWTstring,keysmap[string]ed25519.PublicKey) (*Claims,error) {
342351
tok,err:=jwt.ParseWithClaims(
@@ -348,18 +357,53 @@ func ParseClaims(rawJWT string, keys map[string]ed25519.PublicKey) (*Claims, err
348357
iferr!=nil {
349358
returnnil,err
350359
}
351-
ifclaims,ok:=tok.Claims.(*Claims);ok&&tok.Valid {
360+
returnvalidateClaims(tok)
361+
}
362+
363+
funcvalidateClaims(tok*jwt.Token) (*Claims,error) {
364+
ifclaims,ok:=tok.Claims.(*Claims);ok {
352365
ifclaims.Version!=uint64(CurrentVersion) {
353366
returnnil,ErrInvalidVersion
354367
}
355368
ifclaims.LicenseExpires==nil {
356369
returnnil,ErrMissingLicenseExpires
357370
}
371+
ifclaims.ExpiresAt==nil {
372+
returnnil,ErrMissingExp
373+
}
358374
returnclaims,nil
359375
}
360376
returnnil,xerrors.New("unable to parse Claims")
361377
}
362378

379+
// ParseClaimsIgnoreNbf validates a raw JWT, but ignores `nbf` claim. If otherwise valid, it returns
380+
// the claims. If unparsable or invalid, it returns an error. Ignoring the `nbf` (not before) is
381+
// useful to determine if a JWT _will_ become valid at any point now or in the future.
382+
funcParseClaimsIgnoreNbf(rawJWTstring,keysmap[string]ed25519.PublicKey) (*Claims,error) {
383+
tok,err:=jwt.ParseWithClaims(
384+
rawJWT,
385+
&Claims{},
386+
keyFunc(keys),
387+
jwt.WithValidMethods(ValidMethods),
388+
)
389+
varvErr*jwt.ValidationError
390+
ifxerrors.As(err,&vErr) {
391+
// zero out the NotValidYet error to check if there were other problems
392+
vErr.Errors=vErr.Errors& (^jwt.ValidationErrorNotValidYet)
393+
ifvErr.Errors!=0 {
394+
// There are other errors besides not being valid yet. We _could_ go
395+
// through all the jwt.ValidationError bits and try to work out the
396+
// correct error, but if we get here something very strange is
397+
// going on so let's just return a generic error that says to get in
398+
// touch with our support team.
399+
returnnil,ErrMultipleIssues
400+
}
401+
}elseiferr!=nil {
402+
returnnil,err
403+
}
404+
returnvalidateClaims(tok)
405+
}
406+
363407
funckeyFunc(keysmap[string]ed25519.PublicKey)func(*jwt.Token) (interface{},error) {
364408
returnfunc(j*jwt.Token) (interface{},error) {
365409
keyID,ok:=j.Header[HeaderKeyID].(string)

‎enterprise/coderd/license/license_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -826,6 +826,25 @@ func TestLicenseEntitlements(t *testing.T) {
826826
assert.True(t,entitlements.Features[codersdk.FeatureCustomRoles].Enabled,"custom-roles enabled for premium")
827827
},
828828
},
829+
{
830+
Name:"CurrentAndFuture",
831+
Licenses: []*coderdenttest.LicenseOptions{
832+
enterpriseLicense().UserLimit(100),
833+
premiumLicense().UserLimit(200).FutureTerm(time.Now()),
834+
},
835+
Enablements:defaultEnablements,
836+
AssertEntitlements:func(t*testing.T,entitlements codersdk.Entitlements) {
837+
assertEnterpriseFeatures(t,entitlements)
838+
assertNoErrors(t,entitlements)
839+
assertNoWarnings(t,entitlements)
840+
userFeature:=entitlements.Features[codersdk.FeatureUserLimit]
841+
assert.Equalf(t,int64(100),*userFeature.Limit,"user limit")
842+
assert.Equal(t,codersdk.EntitlementNotEntitled,
843+
entitlements.Features[codersdk.FeatureMultipleOrganizations].Entitlement)
844+
assert.Equal(t,codersdk.EntitlementNotEntitled,
845+
entitlements.Features[codersdk.FeatureCustomRoles].Entitlement)
846+
},
847+
},
829848
}
830849

831850
for_,tc:=rangetestCases {

‎enterprise/coderd/licenses.go

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -86,25 +86,7 @@ func (api *API) postLicense(rw http.ResponseWriter, r *http.Request) {
8686
return
8787
}
8888

89-
rawClaims,err:=license.ParseRaw(addLicense.License,api.LicenseKeys)
90-
iferr!=nil {
91-
httpapi.Write(ctx,rw,http.StatusBadRequest, codersdk.Response{
92-
Message:"Invalid license",
93-
Detail:err.Error(),
94-
})
95-
return
96-
}
97-
exp,ok:=rawClaims["exp"].(float64)
98-
if!ok {
99-
httpapi.Write(ctx,rw,http.StatusBadRequest, codersdk.Response{
100-
Message:"Invalid license",
101-
Detail:"exp claim missing or not parsable",
102-
})
103-
return
104-
}
105-
expTime:=time.Unix(int64(exp),0)
106-
107-
claims,err:=license.ParseClaims(addLicense.License,api.LicenseKeys)
89+
claims,err:=license.ParseClaimsIgnoreNbf(addLicense.License,api.LicenseKeys)
10890
iferr!=nil {
10991
httpapi.Write(ctx,rw,http.StatusBadRequest, codersdk.Response{
11092
Message:"Invalid license",
@@ -134,7 +116,7 @@ func (api *API) postLicense(rw http.ResponseWriter, r *http.Request) {
134116
dl,err:=api.Database.InsertLicense(ctx, database.InsertLicenseParams{
135117
UploadedAt:dbtime.Now(),
136118
JWT:addLicense.License,
137-
Exp:expTime,
119+
Exp:claims.ExpiresAt.Time,
138120
UUID:id,
139121
})
140122
iferr!=nil {
@@ -160,7 +142,15 @@ func (api *API) postLicense(rw http.ResponseWriter, r *http.Request) {
160142
// don't fail the HTTP request, since we did write it successfully to the database
161143
}
162144

163-
httpapi.Write(ctx,rw,http.StatusCreated,convertLicense(dl,rawClaims))
145+
c,err:=decodeClaims(dl)
146+
iferr!=nil {
147+
httpapi.Write(ctx,rw,http.StatusInternalServerError, codersdk.Response{
148+
Message:"Failed to decode database response",
149+
Detail:err.Error(),
150+
})
151+
return
152+
}
153+
httpapi.Write(ctx,rw,http.StatusCreated,convertLicense(dl,c))
164154
}
165155

166156
// postRefreshEntitlements forces an `updateEntitlements` call and publishes

‎enterprise/coderd/licenses_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"net/http"
66
"testing"
7+
"time"
78

89
"github.com/google/uuid"
910
"github.com/stretchr/testify/assert"
@@ -82,6 +83,53 @@ func TestPostLicense(t *testing.T) {
8283
t.Error("expected to get error status 400")
8384
}
8485
})
86+
87+
// Test a license that isn't yet valid, but will be in the future. We should allow this so that
88+
// operators can upload a license ahead of time.
89+
t.Run("NotYet",func(t*testing.T) {
90+
t.Parallel()
91+
client,_:=coderdenttest.New(t,&coderdenttest.Options{DontAddLicense:true})
92+
respLic:=coderdenttest.AddLicense(t,client, coderdenttest.LicenseOptions{
93+
AccountType:license.AccountTypeSalesforce,
94+
AccountID:"testing",
95+
Features: license.Features{
96+
codersdk.FeatureAuditLog:1,
97+
},
98+
NotBefore:time.Now().Add(time.Hour),
99+
GraceAt:time.Now().Add(2*time.Hour),
100+
ExpiresAt:time.Now().Add(3*time.Hour),
101+
})
102+
assert.GreaterOrEqual(t,respLic.ID,int32(0))
103+
// just a couple spot checks for sanity
104+
assert.Equal(t,"testing",respLic.Claims["account_id"])
105+
features,err:=respLic.FeaturesClaims()
106+
require.NoError(t,err)
107+
assert.EqualValues(t,1,features[codersdk.FeatureAuditLog])
108+
})
109+
110+
// Test we still reject a license that isn't valid yet, but has other issues (e.g. expired
111+
// before it starts).
112+
t.Run("NotEver",func(t*testing.T) {
113+
t.Parallel()
114+
client,_:=coderdenttest.New(t,&coderdenttest.Options{DontAddLicense:true})
115+
lic:=coderdenttest.GenerateLicense(t, coderdenttest.LicenseOptions{
116+
AccountType:license.AccountTypeSalesforce,
117+
AccountID:"testing",
118+
Features: license.Features{
119+
codersdk.FeatureAuditLog:1,
120+
},
121+
NotBefore:time.Now().Add(time.Hour),
122+
GraceAt:time.Now().Add(2*time.Hour),
123+
ExpiresAt:time.Now().Add(-time.Hour),
124+
})
125+
_,err:=client.AddLicense(context.Background(), codersdk.AddLicenseRequest{
126+
License:lic,
127+
})
128+
errResp:=&codersdk.Error{}
129+
require.ErrorAs(t,err,&errResp)
130+
require.Equal(t,http.StatusBadRequest,errResp.StatusCode())
131+
require.Contains(t,errResp.Detail,license.ErrMultipleIssues.Error())
132+
})
85133
}
86134

87135
funcTestGetLicense(t*testing.T) {

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp