diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index e8bf641dd5..4df6cee00c 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -211,7 +211,7 @@ CREATE TABLE licenses ( uploaded_at timestamp with time zone NOT NULL, jwt text NOT NULL, exp timestamp with time zone NOT NULL, - uuid uuid + uuid uuid NOT NULL ); COMMENT ON COLUMN licenses.exp IS 'exp tracks the claim of the same name in the JWT, and we include it here so that we can easily query for licenses that have not yet expired.'; diff --git a/coderd/database/migrations/000097_license_not_null_uuid.down.sql b/coderd/database/migrations/000097_license_not_null_uuid.down.sql new file mode 100644 index 0000000000..3ede10f772 --- /dev/null +++ b/coderd/database/migrations/000097_license_not_null_uuid.down.sql @@ -0,0 +1 @@ +ALTER TABLE ONLY licenses ALTER COLUMN uuid DROP NOT NULL; diff --git a/coderd/database/migrations/000097_license_not_null_uuid.up.sql b/coderd/database/migrations/000097_license_not_null_uuid.up.sql new file mode 100644 index 0000000000..ca64a6850b --- /dev/null +++ b/coderd/database/migrations/000097_license_not_null_uuid.up.sql @@ -0,0 +1,8 @@ +BEGIN; + +-- We need to assign uuids to any existing licenses that don't have them. +UPDATE licenses SET uuid = gen_random_uuid() WHERE uuid IS NULL; +-- Assert no licenses have null uuids. +ALTER TABLE ONLY licenses ALTER COLUMN uuid SET NOT NULL; + +COMMIT; diff --git a/coderd/database/models.go b/coderd/database/models.go index ce5d2204a7..23c8029f9f 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -1288,8 +1288,8 @@ type License struct { UploadedAt time.Time `db:"uploaded_at" json:"uploaded_at"` JWT string `db:"jwt" json:"jwt"` // exp tracks the claim of the same name in the JWT, and we include it here so that we can easily query for licenses that have not yet expired. - Exp time.Time `db:"exp" json:"exp"` - Uuid uuid.NullUUID `db:"uuid" json:"uuid"` + Exp time.Time `db:"exp" json:"exp"` + UUID uuid.UUID `db:"uuid" json:"uuid"` } type Organization struct { diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 9ed8530997..a1806b1289 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -1363,7 +1363,7 @@ func (q *sqlQuerier) GetLicenses(ctx context.Context) ([]License, error) { &i.UploadedAt, &i.JWT, &i.Exp, - &i.Uuid, + &i.UUID, ); err != nil { return nil, err } @@ -1399,7 +1399,7 @@ func (q *sqlQuerier) GetUnexpiredLicenses(ctx context.Context) ([]License, error &i.UploadedAt, &i.JWT, &i.Exp, - &i.Uuid, + &i.UUID, ); err != nil { return nil, err } @@ -1427,10 +1427,10 @@ VALUES ` type InsertLicenseParams struct { - UploadedAt time.Time `db:"uploaded_at" json:"uploaded_at"` - JWT string `db:"jwt" json:"jwt"` - Exp time.Time `db:"exp" json:"exp"` - Uuid uuid.NullUUID `db:"uuid" json:"uuid"` + UploadedAt time.Time `db:"uploaded_at" json:"uploaded_at"` + JWT string `db:"jwt" json:"jwt"` + Exp time.Time `db:"exp" json:"exp"` + UUID uuid.UUID `db:"uuid" json:"uuid"` } func (q *sqlQuerier) InsertLicense(ctx context.Context, arg InsertLicenseParams) (License, error) { @@ -1438,7 +1438,7 @@ func (q *sqlQuerier) InsertLicense(ctx context.Context, arg InsertLicenseParams) arg.UploadedAt, arg.JWT, arg.Exp, - arg.Uuid, + arg.UUID, ) var i License err := row.Scan( @@ -1446,7 +1446,7 @@ func (q *sqlQuerier) InsertLicense(ctx context.Context, arg InsertLicenseParams) &i.UploadedAt, &i.JWT, &i.Exp, - &i.Uuid, + &i.UUID, ) return i, err } diff --git a/coderd/database/sqlc.yaml b/coderd/database/sqlc.yaml index e53fead06d..e708a6c4ca 100644 --- a/coderd/database/sqlc.yaml +++ b/coderd/database/sqlc.yaml @@ -43,6 +43,7 @@ overrides: troubleshooting_url: TroubleshootingURL default_ttl: DefaultTTL motd_file: MOTDFile + uuid: UUID sql: - schema: "./dump.sql" diff --git a/coderd/telemetry/telemetry.go b/coderd/telemetry/telemetry.go index f2b8573a54..eeac2286ae 100644 --- a/coderd/telemetry/telemetry.go +++ b/coderd/telemetry/telemetry.go @@ -645,7 +645,7 @@ func ConvertTemplateVersion(version database.TemplateVersion) TemplateVersion { func ConvertLicense(license database.License) License { return License{ UploadedAt: license.UploadedAt, - UUID: license.Uuid.UUID, + UUID: license.UUID, } } diff --git a/coderd/telemetry/telemetry_test.go b/coderd/telemetry/telemetry_test.go index 6df7f95118..cdd1d42f8b 100644 --- a/coderd/telemetry/telemetry_test.go +++ b/coderd/telemetry/telemetry_test.go @@ -71,10 +71,7 @@ func TestTelemetry(t *testing.T) { UploadedAt: database.Now(), JWT: "", Exp: database.Now().Add(time.Hour), - Uuid: uuid.NullUUID{ - UUID: uuid.New(), - Valid: true, - }, + UUID: uuid.New(), }) assert.NoError(t, err) _, snapshot := collectSnapshot(t, db) diff --git a/enterprise/coderd/coderdenttest/coderdenttest.go b/enterprise/coderd/coderdenttest/coderdenttest.go index 0e5c8e7a1e..4dab0efbb3 100644 --- a/enterprise/coderd/coderdenttest/coderdenttest.go +++ b/enterprise/coderd/coderdenttest/coderdenttest.go @@ -11,6 +11,7 @@ import ( "time" "github.com/golang-jwt/jwt/v4" + "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -128,6 +129,7 @@ func GenerateLicense(t *testing.T, options LicenseOptions) string { c := &license.Claims{ RegisteredClaims: jwt.RegisteredClaims{ + ID: uuid.NewString(), Issuer: "test@testing.test", ExpiresAt: jwt.NewNumericDate(options.ExpiresAt), NotBefore: jwt.NewNumericDate(time.Now().Add(-time.Minute)), diff --git a/enterprise/coderd/licenses.go b/enterprise/coderd/licenses.go index ef3f6c9d44..40f45e1b75 100644 --- a/enterprise/coderd/licenses.go +++ b/enterprise/coderd/licenses.go @@ -98,14 +98,19 @@ func (api *API) postLicense(rw http.ResponseWriter, r *http.Request) { } id, err := uuid.Parse(claims.ID) + if err != nil { + // If no uuid is in the license, we generate a random uuid. + // This is not ideal, and this should be fixed to require a uuid + // for all licenses. We require this patch to support older licenses. + // TODO: In the future (April 2023?) we should remove this and reissue + // old licenses with a uuid. + id = uuid.New() + } dl, err := api.Database.InsertLicense(ctx, database.InsertLicenseParams{ UploadedAt: database.Now(), JWT: addLicense.License, Exp: expTime, - Uuid: uuid.NullUUID{ - UUID: id, - Valid: err == nil, - }, + UUID: id, }) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ @@ -229,7 +234,7 @@ func (api *API) deleteLicense(rw http.ResponseWriter, r *http.Request) { func convertLicense(dl database.License, c jwt.MapClaims) codersdk.License { return codersdk.License{ ID: dl.ID, - UUID: dl.Uuid.UUID, + UUID: dl.UUID, UploadedAt: dl.UploadedAt, Claims: c, } diff --git a/enterprise/trialer/trialer.go b/enterprise/trialer/trialer.go index 48b67a9b95..69d395e5a3 100644 --- a/enterprise/trialer/trialer.go +++ b/enterprise/trialer/trialer.go @@ -64,14 +64,14 @@ func New(db database.Store, url string, keys map[string]ed25519.PublicKey) func( return xerrors.Errorf("parse claims: %w", err) } id, err := uuid.Parse(claims.ID) + if err != nil { + return xerrors.Errorf("parse uuid: %w", err) + } _, err = db.InsertLicense(ctx, database.InsertLicenseParams{ UploadedAt: database.Now(), JWT: string(raw), Exp: expTime, - Uuid: uuid.NullUUID{ - UUID: id, - Valid: err == nil, - }, + UUID: id, }) if err != nil { return xerrors.Errorf("insert license: %w", err)