Stub out licensing API (#810)

- Add config option for license key.
- Define license details data structure.
- Include license details in app config API responses.

Currently any non-empty value for `--license_key` behaves as though the
installation is licensed for `basic`. If the license key is empty,
`core` is returned.

Still to come is the appropriate parsing for the license key.
This commit is contained in:
Zach Wasserman 2021-05-19 17:29:38 -07:00 committed by GitHub
parent 72882e8f9f
commit 83b7f79699
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 71 additions and 5 deletions

View file

@ -16,6 +16,7 @@ import (
"github.com/WatchBeam/clock"
"github.com/e-dard/netbug"
"github.com/fleetdm/fleet/ee/licensing"
"github.com/fleetdm/fleet/server/config"
"github.com/fleetdm/fleet/server/datastore/mysql"
"github.com/fleetdm/fleet/server/datastore/s3"
@ -71,6 +72,14 @@ the way that the Fleet server works.
applyDevFlags(&config)
}
license, err := licensing.LoadLicense(config.License.Key)
if err != nil {
initFatal(
err,
"failed to load license",
)
}
var logger kitlog.Logger
{
output := os.Stderr
@ -137,7 +146,6 @@ the way that the Fleet server works.
var ds kolide.Datastore
var carveStore kolide.CarveStore
var err error
mailService := mail.NewService()
ds, err = mysql.New(config.Mysql, clock.C, mysql.Logger(logger))
@ -220,7 +228,7 @@ the way that the Fleet server works.
liveQueryStore := live_query.NewRedisLiveQuery(redisPool)
ssoSessionStore := sso.NewSessionStore(redisPool)
svc, err := service.NewService(ds, resultStore, logger, config, mailService, clock.C, ssoSessionStore, liveQueryStore, carveStore)
svc, err := service.NewService(ds, resultStore, logger, config, mailService, clock.C, ssoSessionStore, liveQueryStore, carveStore, *license)
if err != nil {
initFatal(err, "initializing service")
}

15
ee/licensing/licensing.go Normal file
View file

@ -0,0 +1,15 @@
package licensing
import (
"github.com/fleetdm/fleet/server/kolide"
)
func LoadLicense(licenseKey string) (*kolide.LicenseInfo, error) {
// TODO actual logic here
if licenseKey == "" {
return &kolide.LicenseInfo{Tier: "core"}, nil
}
return &kolide.LicenseInfo{Tier: "basic"}, nil
}

View file

@ -155,6 +155,11 @@ type FilesystemConfig struct {
EnableLogCompression bool `yaml:"enable_log_compression"`
}
// LicenseConfig defines configs related to licensing Fleet.
type LicenseConfig struct {
Key string `yaml:"key"`
}
// KolideConfig stores the application configuration. Each subcategory is
// broken up into it's own struct, defined above. When editing any of these
// structs, Manager.addConfigs and Manager.LoadConfig should be
@ -174,6 +179,7 @@ type KolideConfig struct {
S3 S3Config
PubSub PubSubConfig
Filesystem FilesystemConfig
License LicenseConfig
}
// addConfigs adds the configuration keys and default values that will be
@ -342,6 +348,9 @@ func (man Manager) addConfigs() {
"Enable automatic rotation for osquery log files")
man.addConfigBool("filesystem.enable_log_compression", false,
"Enable compression for the rotated osquery log files")
// License
man.addConfigString("license.key", "", "Fleet license key (to enable Fleet Basic features)")
}
// LoadConfig will load the config variables into a fully initialized
@ -479,6 +488,9 @@ func (man Manager) LoadConfig() KolideConfig {
EnableLogRotation: man.getConfigBool("filesystem.enable_log_rotation"),
EnableLogCompression: man.getConfigBool("filesystem.enable_log_compression"),
},
License: LicenseConfig{
Key: man.getConfigString("license.key"),
},
}
}

View file

@ -47,6 +47,9 @@ type AppConfigService interface {
// Version returns version and build information.
Version(ctx context.Context) (*version.Info, error)
// License returns the licensing information.
License(ctx context.Context) (*LicenseInfo, error)
}
// SMTP settings names returned from API, these map to SMTPAuthType and
@ -328,3 +331,15 @@ type EnrollSecretSpec struct {
// Secrets is the list of enroll secrets.
Secrets []EnrollSecret `json:"secrets"`
}
// LicenseInfo contains information about the Fleet license.
type LicenseInfo struct {
// Tier is the license tier (currently "core" or "basic")
Tier string `json:"tier"`
// Organization is the name of the licensed organization.
Organization string `json:"organization,omitempty"`
// DeviceCount is the number of licensed devices.
DeviceCount int `json:"device_count,omitempty"`
// Expiration is when the license expires.
Expiration *time.Time `json:"expiration,omitempty"`
}

View file

@ -21,6 +21,7 @@ type appConfigResponse struct {
SSOSettings *kolide.SSOSettingsPayload `json:"sso_settings,omitempty"`
HostExpirySettings *kolide.HostExpirySettings `json:"host_expiry_settings,omitempty"`
HostSettings *kolide.HostSettings `json:"host_settings,omitempty"`
License *kolide.LicenseInfo `json:"license,omitempty"`
Err error `json:"error,omitempty"`
}
@ -36,6 +37,10 @@ func makeGetAppConfigEndpoint(svc kolide.Service) endpoint.Endpoint {
if err != nil {
return nil, err
}
license, err := svc.License(ctx)
if err != nil {
return nil, err
}
var smtpSettings *kolide.SMTPSettingsPayload
var ssoSettings *kolide.SSOSettingsPayload
var hostExpirySettings *kolide.HostExpirySettings
@ -75,6 +80,7 @@ func makeGetAppConfigEndpoint(svc kolide.Service) endpoint.Endpoint {
HostSettings: &kolide.HostSettings{
AdditionalQueries: config.AdditionalQueries,
},
License: license,
}
return response, nil
}

View file

@ -22,7 +22,8 @@ import (
// NewService creates a new service from the config struct
func NewService(ds kolide.Datastore, resultStore kolide.QueryResultStore,
logger kitlog.Logger, config config.KolideConfig, mailService kolide.MailService,
c clock.Clock, sso sso.SessionStore, lq kolide.LiveQueryStore, carveStore kolide.CarveStore) (kolide.Service, error) {
c clock.Clock, sso sso.SessionStore, lq kolide.LiveQueryStore, carveStore kolide.CarveStore,
license kolide.LicenseInfo) (kolide.Service, error) {
var svc kolide.Service
osqueryLogger, err := logging.New(config, logger)
@ -45,6 +46,7 @@ func NewService(ds kolide.Datastore, resultStore kolide.QueryResultStore,
metaDataClient: &http.Client{
Timeout: 5 * time.Second,
},
license: license,
}
svc = validationMiddleware{svc, ds, sso}
return svc, nil
@ -66,6 +68,8 @@ type service struct {
metaDataClient *http.Client
seenHostSet *seenHostSet
license kolide.LicenseInfo
}
func (s service) SendEmail(mail kolide.Email) error {

View file

@ -265,3 +265,7 @@ func (svc service) Version(ctx context.Context) (*version.Info, error) {
info := version.Version()
return &info, nil
}
func (svc service) License(ctx context.Context) (*kolide.LicenseInfo, error) {
return &svc.license, nil
}

View file

@ -13,12 +13,14 @@ import (
func newTestService(ds kolide.Datastore, rs kolide.QueryResultStore, lq kolide.LiveQueryStore) (kolide.Service, error) {
mailer := &mockMailService{SendEmailFn: func(e kolide.Email) error { return nil }}
return NewService(ds, rs, kitlog.NewNopLogger(), config.TestConfig(), mailer, clock.C, nil, lq, ds)
license := kolide.LicenseInfo{Tier: "core"}
return NewService(ds, rs, kitlog.NewNopLogger(), config.TestConfig(), mailer, clock.C, nil, lq, ds, license)
}
func newTestServiceWithClock(ds kolide.Datastore, rs kolide.QueryResultStore, lq kolide.LiveQueryStore, c clock.Clock) (kolide.Service, error) {
mailer := &mockMailService{SendEmailFn: func(e kolide.Email) error { return nil }}
return NewService(ds, rs, kitlog.NewNopLogger(), config.TestConfig(), mailer, c, nil, lq, ds)
license := kolide.LicenseInfo{Tier: "core"}
return NewService(ds, rs, kitlog.NewNopLogger(), config.TestConfig(), mailer, c, nil, lq, ds, license)
}
func createTestAppConfig(t *testing.T, ds kolide.Datastore) *kolide.AppConfig {