Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update proxy features #45979

Merged
merged 41 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
aa70c2b
Add feature watcher
mcbattirola Aug 27, 2024
b610f4c
Add test
mcbattirola Aug 28, 2024
98b3dc1
Update godocs, fix typos, rename SupportEntitlementsCompatibility to …
mcbattirola Aug 29, 2024
e2f31d3
Godocs; rename start and stop functions
mcbattirola Aug 29, 2024
0b30b7b
Use `Feature` prefix in var names instead of `License`
mcbattirola Aug 29, 2024
6590076
Fix lint
mcbattirola Aug 29, 2024
f446096
Merge branch 'master' of github.com:gravitational/teleport into mcbat…
mcbattirola Aug 29, 2024
479dc8f
Merge branch 'master' of github.com:gravitational/teleport into mcbat…
mcbattirola Aug 30, 2024
77dc083
Fix TestGetWebConfig_LegacyFeatureLimits
mcbattirola Aug 30, 2024
74066c7
Fix TestGetWebConfig_WithEntitlements
mcbattirola Aug 30, 2024
4efe99a
Merge branch 'master' of github.com:gravitational/teleport into mcbat…
mcbattirola Aug 30, 2024
5c8233e
Fix tests and lint
mcbattirola Sep 2, 2024
3c17d7f
Merge branch 'master' of github.com:gravitational/teleport into mcbat…
mcbattirola Sep 2, 2024
05751aa
Remove sync.Once
mcbattirola Sep 2, 2024
239b1f9
Add jitter
mcbattirola Sep 2, 2024
b6ec2b3
Remove Ping call from getUserContext
mcbattirola Sep 2, 2024
d13f2d5
Merge branch 'master' into mcbattirola/update-proxy-features
mcbattirola Sep 4, 2024
663d99c
Move entitlements test to entitlements package
mcbattirola Sep 4, 2024
2899bfa
Apply suggestions from code review: godoc and tests improvement.
mcbattirola Sep 4, 2024
7ab1760
Improve tests
mcbattirola Sep 4, 2024
690dd27
Shadow `t` in EventuallyWithT closure to avoid mistakes
mcbattirola Sep 4, 2024
5d3b39e
Improve startFeatureWatcher godoc
mcbattirola Sep 4, 2024
53c097d
Log features on update
mcbattirola Sep 4, 2024
96997d8
Log features on update
mcbattirola Sep 4, 2024
bcf8cb7
Avoid race condition on test
mcbattirola Sep 4, 2024
274364f
Merge branch 'master' into mcbattirola/update-proxy-features
mcbattirola Sep 9, 2024
789fbff
Merge branch 'master' into mcbattirola/update-proxy-features
mcbattirola Sep 9, 2024
e6abb87
Improve TODO comment
mcbattirola Sep 12, 2024
0785362
Merge branch 'master' of github.com:gravitational/teleport into mcbat…
mcbattirola Sep 12, 2024
e27f758
Use handler config context
mcbattirola Sep 12, 2024
ea34716
Merge branch 'master' of github.com:gravitational/teleport into mcbat…
mcbattirola Sep 25, 2024
f205914
Start feature watcher in NewHandler
mcbattirola Sep 25, 2024
53c2f79
Improve startFeatureWatcher godocs
mcbattirola Sep 25, 2024
fddde0c
Add TODO to unexport SetClusterFeatures
mcbattirola Sep 25, 2024
837b412
Remove featureWatcherReady
mcbattirola Sep 25, 2024
29e7f11
Merge branch 'master' into mcbattirola/update-proxy-features
mcbattirola Sep 30, 2024
85c4cae
Use require in require.EventuallyWithT in cases where an error is not…
mcbattirola Sep 30, 2024
5153530
Merge branch 'mcbattirola/update-proxy-features' of github.com:gravit…
mcbattirola Sep 30, 2024
171deac
Use return of assert.NoError` to return early on require.EventuallyWithT
mcbattirola Sep 30, 2024
667958e
Merge branch 'master' into mcbattirola/update-proxy-features
mcbattirola Sep 30, 2024
423bc2b
Merge branch 'master' of github.com:gravitational/teleport into mcbat…
mcbattirola Oct 1, 2024
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
66 changes: 66 additions & 0 deletions entitlements/entitlements.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package entitlements

import "github.com/gravitational/teleport/api/client/proto"

type EntitlementKind string

// The EntitlementKind list should be 1:1 with the Features & FeatureStrings in salescenter/product/product.go,
Expand Down Expand Up @@ -57,3 +59,67 @@ var AllEntitlements = []EntitlementKind{
ExternalAuditStorage, FeatureHiding, HSM, Identity, JoinActiveSessions, K8s, MobileDeviceManagement, OIDC, OktaSCIM,
OktaUserSync, Policy, SAML, SessionLocks, UpsellAlert, UsageReporting, LicenseAutoUpdate,
}

// BackfillFeatures ensures entitlements are backwards compatible.
// If Entitlements are present, there are no changes.
// If Entitlements are not present, it sets the entitlements based on legacy field values.
// TODO(michellescripts) DELETE IN 18.0.0
func BackfillFeatures(features *proto.Features) {
if len(features.Entitlements) > 0 {
return
}

features.Entitlements = getBaseEntitlements(features.GetEntitlements())

// Entitlements: All records are {enabled: false}; update to equal legacy feature value
features.Entitlements[string(ExternalAuditStorage)] = &proto.EntitlementInfo{Enabled: features.GetExternalAuditStorage()}
features.Entitlements[string(FeatureHiding)] = &proto.EntitlementInfo{Enabled: features.GetFeatureHiding()}
features.Entitlements[string(Identity)] = &proto.EntitlementInfo{Enabled: features.GetIdentityGovernance()}
features.Entitlements[string(JoinActiveSessions)] = &proto.EntitlementInfo{Enabled: features.GetJoinActiveSessions()}
features.Entitlements[string(MobileDeviceManagement)] = &proto.EntitlementInfo{Enabled: features.GetMobileDeviceManagement()}
features.Entitlements[string(OIDC)] = &proto.EntitlementInfo{Enabled: features.GetOIDC()}
features.Entitlements[string(Policy)] = &proto.EntitlementInfo{Enabled: features.GetPolicy().GetEnabled()}
features.Entitlements[string(SAML)] = &proto.EntitlementInfo{Enabled: features.GetSAML()}
features.Entitlements[string(K8s)] = &proto.EntitlementInfo{Enabled: features.GetKubernetes()}
features.Entitlements[string(App)] = &proto.EntitlementInfo{Enabled: features.GetApp()}
features.Entitlements[string(DB)] = &proto.EntitlementInfo{Enabled: features.GetDB()}
features.Entitlements[string(Desktop)] = &proto.EntitlementInfo{Enabled: features.GetDesktop()}
features.Entitlements[string(HSM)] = &proto.EntitlementInfo{Enabled: features.GetHSM()}

// set default Identity fields to legacy feature value
features.Entitlements[string(AccessLists)] = &proto.EntitlementInfo{Enabled: true, Limit: features.GetAccessList().GetCreateLimit()}
features.Entitlements[string(AccessMonitoring)] = &proto.EntitlementInfo{Enabled: features.GetAccessMonitoring().GetEnabled(), Limit: features.GetAccessMonitoring().GetMaxReportRangeLimit()}
features.Entitlements[string(AccessRequests)] = &proto.EntitlementInfo{Enabled: features.GetAccessRequests().MonthlyRequestLimit > 0, Limit: features.GetAccessRequests().GetMonthlyRequestLimit()}
features.Entitlements[string(DeviceTrust)] = &proto.EntitlementInfo{Enabled: features.GetDeviceTrust().GetEnabled(), Limit: features.GetDeviceTrust().GetDevicesUsageLimit()}
// override Identity Package features if Identity is enabled: set true and clear limit
if features.GetIdentityGovernance() {
features.Entitlements[string(AccessLists)] = &proto.EntitlementInfo{Enabled: true}
features.Entitlements[string(AccessMonitoring)] = &proto.EntitlementInfo{Enabled: true}
features.Entitlements[string(AccessRequests)] = &proto.EntitlementInfo{Enabled: true}
features.Entitlements[string(DeviceTrust)] = &proto.EntitlementInfo{Enabled: true}
features.Entitlements[string(OktaSCIM)] = &proto.EntitlementInfo{Enabled: true}
features.Entitlements[string(OktaUserSync)] = &proto.EntitlementInfo{Enabled: true}
features.Entitlements[string(SessionLocks)] = &proto.EntitlementInfo{Enabled: true}
}
}

// getBaseEntitlements takes a cloud entitlement set and returns a modules Entitlement set
func getBaseEntitlements(protoEntitlements map[string]*proto.EntitlementInfo) map[string]*proto.EntitlementInfo {
all := AllEntitlements
result := make(map[string]*proto.EntitlementInfo, len(all))

for _, e := range all {
al, ok := protoEntitlements[string(e)]
if !ok {
result[string(e)] = &proto.EntitlementInfo{}
continue
}

result[string(e)] = &proto.EntitlementInfo{
Enabled: al.Enabled,
Limit: al.Limit,
}
}

return result
}
286 changes: 286 additions & 0 deletions entitlements/entitlements_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
// Teleport
// Copyright (C) 2024 Gravitational, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package entitlements

import (
"testing"

"github.com/stretchr/testify/require"

"github.com/gravitational/teleport/api/client/proto"
apiutils "github.com/gravitational/teleport/api/utils"
)

func TestBackfillFeatures(t *testing.T) {
tests := []struct {
name string
features *proto.Features
expected map[string]*proto.EntitlementInfo
}{
{
name: "entitlements present; keeps entitlement values",
features: &proto.Features{
DeviceTrust: nil,
AccessRequests: nil,
AccessList: nil,
AccessMonitoring: nil,
Policy: nil,
CustomTheme: "",
ProductType: 0,
SupportType: 0,
Kubernetes: false,
App: false,
DB: false,
OIDC: false,
SAML: false,
AccessControls: false,
AdvancedAccessWorkflows: false,
Cloud: false,
HSM: false,
Desktop: false,
RecoveryCodes: false,
Plugins: false,
AutomaticUpgrades: false,
IsUsageBased: false,
Assist: false,
FeatureHiding: false,
IdentityGovernance: false,
AccessGraph: false,
Questionnaire: false,
IsStripeManaged: false,
ExternalAuditStorage: false,
JoinActiveSessions: false,
MobileDeviceManagement: false,
AccessMonitoringConfigured: false,
Entitlements: map[string]*proto.EntitlementInfo{
string(AccessLists): {Enabled: true, Limit: 111},
string(AccessMonitoring): {Enabled: true, Limit: 2113},
string(AccessRequests): {Enabled: true, Limit: 39},
string(App): {Enabled: false},
string(CloudAuditLogRetention): {Enabled: true},
string(DB): {Enabled: true},
string(Desktop): {Enabled: true},
string(DeviceTrust): {Enabled: true, Limit: 103},
string(ExternalAuditStorage): {Enabled: true},
string(FeatureHiding): {Enabled: true},
string(HSM): {Enabled: true},
string(Identity): {Enabled: true},
string(JoinActiveSessions): {Enabled: true},
string(K8s): {Enabled: true},
string(MobileDeviceManagement): {Enabled: true},
string(OIDC): {Enabled: true},
string(OktaSCIM): {Enabled: true},
string(OktaUserSync): {Enabled: true},
string(Policy): {Enabled: true},
string(SAML): {Enabled: true},
string(SessionLocks): {Enabled: true},
string(UpsellAlert): {Enabled: true},
string(UsageReporting): {Enabled: true},
string(LicenseAutoUpdate): {Enabled: true},
},
},
expected: map[string]*proto.EntitlementInfo{
string(AccessLists): {Enabled: true, Limit: 111},
string(AccessMonitoring): {Enabled: true, Limit: 2113},
string(AccessRequests): {Enabled: true, Limit: 39},
string(App): {Enabled: false},
string(CloudAuditLogRetention): {Enabled: true},
string(DB): {Enabled: true},
string(Desktop): {Enabled: true},
string(DeviceTrust): {Enabled: true, Limit: 103},
string(ExternalAuditStorage): {Enabled: true},
string(FeatureHiding): {Enabled: true},
string(HSM): {Enabled: true},
string(Identity): {Enabled: true},
string(JoinActiveSessions): {Enabled: true},
string(K8s): {Enabled: true},
string(MobileDeviceManagement): {Enabled: true},
string(OIDC): {Enabled: true},
string(OktaSCIM): {Enabled: true},
string(OktaUserSync): {Enabled: true},
string(Policy): {Enabled: true},
string(SAML): {Enabled: true},
string(SessionLocks): {Enabled: true},
string(UpsellAlert): {Enabled: true},
string(UsageReporting): {Enabled: true},
string(LicenseAutoUpdate): {Enabled: true},
},
},
{
name: "entitlements not present; identity on - sets legacy fields & drops limits",
features: &proto.Features{
DeviceTrust: &proto.DeviceTrustFeature{
Enabled: true,
DevicesUsageLimit: 33,
},
AccessRequests: &proto.AccessRequestsFeature{
MonthlyRequestLimit: 22,
},
AccessList: &proto.AccessListFeature{
CreateLimit: 44,
},
AccessMonitoring: &proto.AccessMonitoringFeature{
Enabled: true,
MaxReportRangeLimit: 55,
},
Policy: &proto.PolicyFeature{
Enabled: true,
},
CustomTheme: "",
ProductType: 0,
SupportType: 0,
Kubernetes: true,
App: true,
DB: true,
OIDC: true,
SAML: true,
AccessControls: true,
AdvancedAccessWorkflows: true,
Cloud: true,
HSM: true,
Desktop: true,
RecoveryCodes: true,
Plugins: true,
AutomaticUpgrades: true,
IsUsageBased: true,
Assist: true,
FeatureHiding: true,
IdentityGovernance: true,
AccessGraph: true,
Questionnaire: true,
IsStripeManaged: true,
ExternalAuditStorage: true,
JoinActiveSessions: true,
MobileDeviceManagement: true,
AccessMonitoringConfigured: true,
},
expected: map[string]*proto.EntitlementInfo{
string(AccessLists): {Enabled: true},
string(AccessMonitoring): {Enabled: true},
string(AccessRequests): {Enabled: true},
string(App): {Enabled: true},
string(DB): {Enabled: true},
string(Desktop): {Enabled: true},
string(DeviceTrust): {Enabled: true},
string(ExternalAuditStorage): {Enabled: true},
string(FeatureHiding): {Enabled: true},
string(HSM): {Enabled: true},
string(Identity): {Enabled: true},
string(JoinActiveSessions): {Enabled: true},
string(K8s): {Enabled: true},
string(MobileDeviceManagement): {Enabled: true},
string(OIDC): {Enabled: true},
string(OktaSCIM): {Enabled: true},
string(OktaUserSync): {Enabled: true},
string(Policy): {Enabled: true},
string(SAML): {Enabled: true},
string(SessionLocks): {Enabled: true},
// defaults, no legacy equivalent
string(UsageReporting): {Enabled: false},
string(UpsellAlert): {Enabled: false},
string(CloudAuditLogRetention): {Enabled: false},
string(LicenseAutoUpdate): {Enabled: false},
},
},
{
name: "entitlements not present; identity off - sets legacy fields",
features: &proto.Features{
DeviceTrust: &proto.DeviceTrustFeature{
Enabled: true,
DevicesUsageLimit: 33,
},
AccessRequests: &proto.AccessRequestsFeature{
MonthlyRequestLimit: 22,
},
AccessList: &proto.AccessListFeature{
CreateLimit: 44,
},
AccessMonitoring: &proto.AccessMonitoringFeature{
Enabled: true,
MaxReportRangeLimit: 55,
},
Policy: &proto.PolicyFeature{
Enabled: true,
},
CustomTheme: "",
ProductType: 0,
SupportType: 0,
Kubernetes: true,
App: true,
DB: true,
OIDC: true,
SAML: true,
AccessControls: true,
AdvancedAccessWorkflows: true,
Cloud: true,
HSM: true,
Desktop: true,
RecoveryCodes: true,
Plugins: true,
AutomaticUpgrades: true,
IsUsageBased: true,
Assist: true,
FeatureHiding: true,
IdentityGovernance: false,
AccessGraph: true,
Questionnaire: true,
IsStripeManaged: true,
ExternalAuditStorage: true,
JoinActiveSessions: true,
MobileDeviceManagement: true,
AccessMonitoringConfigured: true,
},
expected: map[string]*proto.EntitlementInfo{
string(AccessLists): {Enabled: true, Limit: 44},
string(AccessMonitoring): {Enabled: true, Limit: 55},
string(AccessRequests): {Enabled: true, Limit: 22},
string(DeviceTrust): {Enabled: true, Limit: 33},
string(App): {Enabled: true},
string(DB): {Enabled: true},
string(Desktop): {Enabled: true},
string(ExternalAuditStorage): {Enabled: true},
string(FeatureHiding): {Enabled: true},
string(HSM): {Enabled: true},
string(JoinActiveSessions): {Enabled: true},
string(K8s): {Enabled: true},
string(MobileDeviceManagement): {Enabled: true},
string(OIDC): {Enabled: true},
string(Policy): {Enabled: true},
string(SAML): {Enabled: true},

// defaults, no legacy equivalent
string(UsageReporting): {Enabled: false},
string(UpsellAlert): {Enabled: false},
string(CloudAuditLogRetention): {Enabled: false},
string(LicenseAutoUpdate): {Enabled: false},
// Identity off, fields false
string(Identity): {Enabled: false},
string(SessionLocks): {Enabled: false},
string(OktaSCIM): {Enabled: false},
string(OktaUserSync): {Enabled: false},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cloned := apiutils.CloneProtoMsg(tt.features)

BackfillFeatures(cloned)
require.Equal(t, tt.expected, cloned.Entitlements)
})
}
}
Loading
Loading