From 17666732ebfc35f59c9a3b6bdca9167991ffeb1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edu=20G=C3=B3mez=20Escandell?= Date: Mon, 30 Oct 2023 15:57:24 +0100 Subject: [PATCH 01/16] Remove all registry write functions Also, the OpenKey function now does not take an access argument. We only ever need read access --- .../internal/config/registry/registry.go | 7 -- .../config/registry/registry_linux.go | 17 +---- .../internal/config/registry/registry_mock.go | 76 +------------------ .../config/registry/registry_windows.go | 23 +----- 4 files changed, 6 insertions(+), 117 deletions(-) diff --git a/windows-agent/internal/config/registry/registry.go b/windows-agent/internal/config/registry/registry.go index 6a2ebd1e0..36df48762 100644 --- a/windows-agent/internal/config/registry/registry.go +++ b/windows-agent/internal/config/registry/registry.go @@ -4,13 +4,6 @@ package registry import "errors" -// Key access rights. -// https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-key-security-and-access-rights -const ( - READ = 0x20019 - WRITE = 0x20006 -) - // Although the stdlibs' registry and syscall packages return typed errors, these are // only defined in their _windows files. We convert the relevant ones here to // cross-platform errors. diff --git a/windows-agent/internal/config/registry/registry_linux.go b/windows-agent/internal/config/registry/registry_linux.go index 6f80eebba..363b37d70 100644 --- a/windows-agent/internal/config/registry/registry_linux.go +++ b/windows-agent/internal/config/registry/registry_linux.go @@ -3,13 +3,8 @@ package registry // Windows is the Windows registry. Any interaction with it will panic. type Windows struct{} -// HKCUCreateKey creates a key in the specified path under the HK_CURRENT_USER registry. -func (Windows) HKCUCreateKey(path string, access uint32) (newk uintptr, err error) { - panic("the Windows registry is not available on Linux") -} - // HKCUOpenKey opens a key in the specified path under the HK_CURRENT_USER registry. -func (Windows) HKCUOpenKey(path string, access uint32) (key uintptr, err error) { +func (Windows) HKCUOpenKey(path string) (key uintptr, err error) { panic("the Windows registry is not available on Linux") } @@ -22,13 +17,3 @@ func (Windows) CloseKey(k uintptr) { func (Windows) ReadValue(k uintptr, field string) (value string, err error) { panic("the Windows registry is not available on Linux") } - -// WriteValue writes the provided single-line string value into the specified field of key k. -func (Windows) WriteValue(k uintptr, field string, value string) (err error) { - panic("the Windows registry is not available on Linux") -} - -// WriteMultilineValue writes the provided multi-line string value into the specified field of key k. -func (Windows) WriteMultilineValue(k uintptr, field string, value string) (err error) { - panic("the Windows registry is not available on Linux") -} diff --git a/windows-agent/internal/config/registry/registry_mock.go b/windows-agent/internal/config/registry/registry_mock.go index 9f121127c..fd2b9959f 100644 --- a/windows-agent/internal/config/registry/registry_mock.go +++ b/windows-agent/internal/config/registry/registry_mock.go @@ -16,9 +16,6 @@ type Mock struct { // KeyExists indicates whether the UbuntuPro key already exists in the registry. KeyExists bool - // KeyExists indicates whether the current user has write access to the UbuntuPro key - KeyIsReadOnly bool - // Error triggers so the tests can mock system errors Errors uint32 @@ -42,30 +39,8 @@ func NewMock() *Mock { } } -// HKCUCreateKey mocks creating a key in the specified path under the HK_CURRENT_USER registry. -func (r *Mock) HKCUCreateKey(path string, access uint32) (newk uintptr, err error) { - if path != `Software\Canonical\UbuntuPro` { - panic(`Attempted to access registry outside of HKCU\Software\Canonical\UbuntuPro`) - } - - if r.Errors&MockErrOnCreateKey != 0 { - return newk, ErrMock - } - - if r.KeyIsReadOnly && isWrite(access) { - return 0, ErrAccessDenied - } - - r.KeyExists = true - r.OpenKeyCount.Add(1) - - // Since we always deal with the same key, we return its access permissions - // as that is all the info we need from it. - return uintptr(access), nil -} - // HKCUOpenKey mocks opening a key in the specified path under the HK_CURRENT_USER registry. -func (r *Mock) HKCUOpenKey(path string, access uint32) (uintptr, error) { +func (r *Mock) HKCUOpenKey(path string) (uintptr, error) { if path != `Software\Canonical\UbuntuPro` { panic(`Attempted to access registry outside of HKCU\Software\Canonical\UbuntuPro`) } @@ -78,15 +53,10 @@ func (r *Mock) HKCUOpenKey(path string, access uint32) (uintptr, error) { return 0, ErrKeyNotExist } - if r.KeyIsReadOnly && isWrite(access) { - return 0, ErrAccessDenied - } - r.OpenKeyCount.Add(1) - // Since we always deal with the same key, we return its access permissions - // as that is all the info we need from it. - return uintptr(access), nil + // In the real implementation this integer is a pointer. Here it's just a dummy value. + return 1, nil } // CloseKey mocks releasing a key. @@ -104,49 +74,9 @@ func (r Mock) ReadValue(k uintptr, field string) (value string, err error) { return value, ErrMock } - if !isRead(uint32(k)) { - return value, errors.New("key was not opened with READ access") - } v, ok := r.UbuntuProData[field] if !ok { return v, ErrFieldNotExist } return v, nil } - -func (r *Mock) write(k uintptr, field string, value string) (err error) { - if k == 0 { - return errors.New("Null key") - } - - if r.Errors&MockErrOnWriteValue != 0 { - return ErrMock - } - - if !isWrite(uint32(k)) { - return errors.New("key was not opened with WRITE access") - } - - r.UbuntuProData[field] = value - return nil -} - -// WriteValue writes the provided value into the specified field of key k. -func (r *Mock) WriteValue(k uintptr, field string, value string) (err error) { - return r.write(k, field, value) -} - -// WriteMultilineValue writes the provided multi-line string into the specified field of key k. -func (r *Mock) WriteMultilineValue(k uintptr, field string, value string) (err error) { - return r.write(k, field, value) -} - -func isRead(access uint32) bool { - // Bit mask of the lower 16 bits - return (access & READ & 0xffff) != 0 -} - -func isWrite(access uint32) bool { - // Bit mask of the lower 16 bits - return (access & WRITE & 0xffff) != 0 -} diff --git a/windows-agent/internal/config/registry/registry_windows.go b/windows-agent/internal/config/registry/registry_windows.go index 26c922fc3..f02e6d333 100644 --- a/windows-agent/internal/config/registry/registry_windows.go +++ b/windows-agent/internal/config/registry/registry_windows.go @@ -11,18 +11,9 @@ import ( // Windows is the Windows registry. type Windows struct{} -// HKCUCreateKey creates a key in the specified path under the HK_CURRENT_USER registry. -func (Windows) HKCUCreateKey(path string, access uint32) (newk uintptr, err error) { - key, _, err := registry.CreateKey(registry.CURRENT_USER, path, access) - if errors.Is(err, syscall.Errno(5)) { // Access is denied - return 0, ErrAccessDenied - } - return uintptr(key), err -} - // HKCUOpenKey opens a key in the specified path under the HK_CURRENT_USER registry. -func (Windows) HKCUOpenKey(path string, access uint32) (uintptr, error) { - key, err := registry.OpenKey(registry.CURRENT_USER, path, access) +func (Windows) HKCUOpenKey(path string) (uintptr, error) { + key, err := registry.OpenKey(registry.CURRENT_USER, path, registry.READ) if errors.Is(err, registry.ErrNotExist) { return 0, ErrKeyNotExist } @@ -64,13 +55,3 @@ func (Windows) ReadValue(k uintptr, field string) (string, error) { return "", acc } - -// WriteValue writes the provided single-line string value into the specified field of key k. -func (Windows) WriteValue(k uintptr, field string, value string) error { - return registry.Key(k).SetStringValue(field, value) -} - -// WriteMultilineValue writes the provided multi-line string value into the specified field of key k. -func (Windows) WriteMultilineValue(k uintptr, field string, value string) error { - return registry.Key(k).SetStringsValue(field, strings.Split(value, "\n")) -} From 25a4e16f3a8c3c08f9f2dc5804e3c0bf00b1d319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edu=20G=C3=B3mez=20Escandell?= Date: Mon, 30 Oct 2023 15:59:12 +0100 Subject: [PATCH 02/16] Improve error messages in registry mock --- windows-agent/internal/config/registry/registry_mock.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/windows-agent/internal/config/registry/registry_mock.go b/windows-agent/internal/config/registry/registry_mock.go index fd2b9959f..2f31eeff8 100644 --- a/windows-agent/internal/config/registry/registry_mock.go +++ b/windows-agent/internal/config/registry/registry_mock.go @@ -42,7 +42,7 @@ func NewMock() *Mock { // HKCUOpenKey mocks opening a key in the specified path under the HK_CURRENT_USER registry. func (r *Mock) HKCUOpenKey(path string) (uintptr, error) { if path != `Software\Canonical\UbuntuPro` { - panic(`Attempted to access registry outside of HKCU\Software\Canonical\UbuntuPro`) + panic(`Attempted to access mock registry outside of HKCU\Software\Canonical\UbuntuPro`) } if r.Errors&MockErrOnOpenKey != 0 { @@ -67,7 +67,7 @@ func (r *Mock) CloseKey(k uintptr) { // ReadValue returns the value of the specified field in the specified key. func (r Mock) ReadValue(k uintptr, field string) (value string, err error) { if k == 0 { - return value, errors.New("Null key") + return value, errors.New("null key") } if r.Errors&MockErrReadValue != 0 { From 67a6ed869e320a529cb3f6b35afe22c0e4727c4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edu=20G=C3=B3mez=20Escandell?= Date: Mon, 30 Oct 2023 16:00:23 +0100 Subject: [PATCH 03/16] Rename variable acc to errs This is the standard we've agreed on in the team --- .../internal/config/registry/registry_windows.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/windows-agent/internal/config/registry/registry_windows.go b/windows-agent/internal/config/registry/registry_windows.go index f02e6d333..b63d58f8e 100644 --- a/windows-agent/internal/config/registry/registry_windows.go +++ b/windows-agent/internal/config/registry/registry_windows.go @@ -31,14 +31,14 @@ func (Windows) CloseKey(k uintptr) { // ReadValue returns the value of the specified field in the specified key. func (Windows) ReadValue(k uintptr, field string) (string, error) { - var acc error + var errs error // Try to read single-line string value, _, err := registry.Key(k).GetStringValue(field) if errors.Is(err, registry.ErrNotExist) { return value, ErrFieldNotExist } else if err != nil { - acc = errors.Join(acc, err) + errs = errors.Join(errs, err) } else { return value, nil } @@ -48,10 +48,10 @@ func (Windows) ReadValue(k uintptr, field string) (string, error) { if errors.Is(err, registry.ErrNotExist) { return value, ErrFieldNotExist } else if err != nil { - acc = errors.Join(acc, err) + errs = errors.Join(errs, err) } else { return strings.Join(lines, "\n"), nil } - return "", acc + return "", errs } From 83ce67675d5808180e335f8b225ebcd9a6e7be0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edu=20G=C3=B3mez=20Escandell?= Date: Mon, 30 Oct 2023 16:03:07 +0100 Subject: [PATCH 04/16] Remove Config.IsReadOnly This is no longer relevant, as we never write to the registry. --- windows-agent/internal/config/config.go | 25 --------------------- windows-agent/internal/proservices/ui/ui.go | 10 --------- 2 files changed, 35 deletions(-) diff --git a/windows-agent/internal/config/config.go b/windows-agent/internal/config/config.go index 3a85db403..3b227e846 100644 --- a/windows-agent/internal/config/config.go +++ b/windows-agent/internal/config/config.go @@ -96,21 +96,6 @@ func (c *Config) Subscription(ctx context.Context) (token string, source Source, return token, source, nil } -// IsReadOnly returns whether the registry can be written to. -func (c *Config) IsReadOnly() (b bool, err error) { - // CreateKey is equivalent to OpenKey if the key already existed - k, err := c.registry.HKCUCreateKey(registryPath, registry.WRITE) - if errors.Is(err, registry.ErrAccessDenied) { - return true, nil - } - if err != nil { - return false, fmt.Errorf("could not open registry key: %w", err) - } - - c.registry.CloseKey(k) - return false, nil -} - // ProvisioningTasks returns a slice of all tasks to be submitted upon first contact with a distro. func (c *Config) ProvisioningTasks(ctx context.Context, distroName string) ([]task.Task, error) { var taskList []task.Task @@ -210,16 +195,6 @@ func (c *Config) LandscapeAgentUID(ctx context.Context) (string, error) { func (c *Config) FetchMicrosoftStoreSubscription(ctx context.Context, args ...contracts.Option) (err error) { defer decorate.OnError(&err, "could not validate subscription against Microsoft Store") - readOnly, err := c.IsReadOnly() - if err != nil { - return fmt.Errorf("could not detect if subscription is user-managed: %v", err) - } - - if readOnly { - // No need to contact the store because we cannot change the subscription - return fmt.Errorf("subscription cannot be user-managed") - } - _, src, err := c.Subscription(ctx) if err != nil { return fmt.Errorf("could not get current subscription status: %v", err) diff --git a/windows-agent/internal/proservices/ui/ui.go b/windows-agent/internal/proservices/ui/ui.go index e5dfddd79..48247b343 100644 --- a/windows-agent/internal/proservices/ui/ui.go +++ b/windows-agent/internal/proservices/ui/ui.go @@ -18,7 +18,6 @@ import ( // Config is a provider for the subcription configuration. type Config interface { SetUserSubscription(ctx context.Context, token string) error - IsReadOnly() (bool, error) Subscription(context.Context) (string, config.Source, error) FetchMicrosoftStoreSubscription(context.Context, ...contracts.Option) error } @@ -73,15 +72,6 @@ func (s *Service) Ping(ctx context.Context, request *agentapi.Empty) (*agentapi. func (s *Service) GetSubscriptionInfo(ctx context.Context, empty *agentapi.Empty) (*agentapi.SubscriptionInfo, error) { info := &agentapi.SubscriptionInfo{} - immutable, err := s.config.IsReadOnly() - if err != nil { - return nil, err - } - - if immutable { - info.Immutable = true - } - _, source, err := s.config.Subscription(ctx) if err != nil { return nil, err From c06c620a7e9886736fcaa19974d8c349604602fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edu=20G=C3=B3mez=20Escandell?= Date: Mon, 30 Oct 2023 16:04:28 +0100 Subject: [PATCH 05/16] Remove config.IsReadOnly tests --- windows-agent/internal/config/config_test.go | 79 ++++--------------- .../internal/proservices/proservices_test.go | 8 -- 2 files changed, 17 insertions(+), 70 deletions(-) diff --git a/windows-agent/internal/config/config_test.go b/windows-agent/internal/config/config_test.go index 9ef905819..015e84d29 100644 --- a/windows-agent/internal/config/config_test.go +++ b/windows-agent/internal/config/config_test.go @@ -90,7 +90,7 @@ func TestSubscription(t *testing.T) { t.Parallel() ctx := context.Background() - r, dir := setUpMockSettings(t, tc.mockErrors, tc.settingsState, false, tc.breakFile) + r, dir := setUpMockSettings(t, tc.mockErrors, tc.settingsState, tc.breakFile) conf := config.New(ctx, dir, config.WithRegistry(r)) token, source, err := conf.Subscription(ctx) @@ -142,7 +142,7 @@ func TestLandscapeConfig(t *testing.T) { t.Parallel() ctx := context.Background() - r, dir := setUpMockSettings(t, tc.mockErrors, tc.settingsState, false, tc.breakFile) + r, dir := setUpMockSettings(t, tc.mockErrors, tc.settingsState, tc.breakFile) conf := config.New(ctx, dir, config.WithRegistry(r)) token, source, err := conf.LandscapeClientConfig(ctx) @@ -184,7 +184,7 @@ func TestLandscapeAgentUID(t *testing.T) { t.Parallel() ctx := context.Background() - r, dir := setUpMockSettings(t, 0, tc.settingsState, false, tc.breakFile) + r, dir := setUpMockSettings(t, 0, tc.settingsState, tc.breakFile) if tc.breakFileContents { err := os.WriteFile(filepath.Join(dir, "config"), []byte("\tmessage:\n\t\tthis is not YAML!["), 0600) require.NoError(t, err, "Setup: could not re-write config file") @@ -242,7 +242,7 @@ func TestProvisioningTasks(t *testing.T) { t.Parallel() ctx := context.Background() - r, dir := setUpMockSettings(t, tc.mockErrors, tc.settingsState, false, false) + r, dir := setUpMockSettings(t, tc.mockErrors, tc.settingsState, false) conf := config.New(ctx, dir, config.WithRegistry(r)) gotTasks, err := conf.ProvisioningTasks(ctx, "UBUNTU") @@ -294,7 +294,7 @@ func TestSetUserSubscription(t *testing.T) { t.Parallel() ctx := context.Background() - r, dir := setUpMockSettings(t, 0, tc.settingsState, false, tc.breakFile) + r, dir := setUpMockSettings(t, 0, tc.settingsState, tc.breakFile) conf := config.New(ctx, dir, config.WithRegistry(r)) token := "new_token" @@ -344,7 +344,7 @@ func TestSetLandscapeAgentUID(t *testing.T) { t.Parallel() ctx := context.Background() - r, dir := setUpMockSettings(t, 0, tc.settingsState, false, tc.breakFile) + r, dir := setUpMockSettings(t, 0, tc.settingsState, tc.breakFile) conf := config.New(ctx, dir, config.WithRegistry(r)) uid := "new_uid" @@ -369,51 +369,6 @@ func TestSetLandscapeAgentUID(t *testing.T) { } } -func TestIsReadOnly(t *testing.T) { - t.Parallel() - - testCases := map[string]struct { - settingsState settingsState - readOnly bool - registryErr bool - - want bool - wantErr bool - }{ - "Success when the registry can be written on": {settingsState: keyExists, want: false}, - "Success when the registry cannot be written on": {settingsState: keyExists, readOnly: true, want: true}, - - "Success when the non-existent registry can be written on": {want: false}, - "Success when the non-existent registry cannot be written on": {readOnly: true, want: true}, - - "Error when the registry cannot be queried": {registryErr: true, wantErr: true}, - } - - for name, tc := range testCases { - tc := tc - t.Run(name, func(t *testing.T) { - t.Parallel() - ctx := context.Background() - - r, dir := setUpMockSettings(t, 0, tc.settingsState, tc.readOnly, false) - if tc.registryErr { - r.Errors = registry.MockErrOnCreateKey - } - - conf := config.New(ctx, dir, config.WithRegistry(r)) - - got, err := conf.IsReadOnly() - if tc.wantErr { - require.Error(t, err, "IsReadOnly should return an error") - return - } - require.NoError(t, err, "IsReadOnly should return no error") - - require.Equal(t, tc.want, got, "IsReadOnly returned an unexpected value") - }) - } -} - func TestFetchMicrosoftStoreSubscription(t *testing.T) { t.Parallel() @@ -427,9 +382,8 @@ func TestFetchMicrosoftStoreSubscription(t *testing.T) { settingsState settingsState subscriptionExpired bool - registryErr uint32 - registryIsReadOnly bool - + registryErr uint32 + breakConfigFile bool msStoreJWTErr bool msStoreExpirationErr bool @@ -439,8 +393,6 @@ func TestFetchMicrosoftStoreSubscription(t *testing.T) { // Tests where there is no pre-existing subscription "Success": {wantToken: proToken}, - "Error when registry is read only": {settingsState: userTokenHasValue, registryIsReadOnly: true, wantToken: "user_token", wantErr: true}, - "Error when registry read-only check fails": {registryErr: registry.MockErrOnCreateKey, wantErr: true}, "Error when the Microsoft Store cannot provide the JWT": {msStoreJWTErr: true, wantErr: true}, // Tests where there is a pre-existing subscription @@ -448,6 +400,10 @@ func TestFetchMicrosoftStoreSubscription(t *testing.T) { "Success when there is an expired store token": {settingsState: storeTokenHasValue, subscriptionExpired: true, wantToken: proToken}, "Error when the Microsoft Store cannot provide the expiration date": {settingsState: storeTokenHasValue, msStoreExpirationErr: true, wantToken: "store_token", wantErr: true}, + + // Tests where pre-existing subscription is irrelevant + "Error when the registry cannot be read from": {registryErr: registry.MockErrOnOpenKey, wantErr: true}, + "Error when the current subscription cannot be obtained": {breakConfigFile: true, wantErr: true}, } for name, tc := range testCases { @@ -457,7 +413,7 @@ func TestFetchMicrosoftStoreSubscription(t *testing.T) { ctx := context.Background() - r, dir := setUpMockSettings(t, tc.registryErr, tc.settingsState, tc.registryIsReadOnly, false) + r, dir := setUpMockSettings(t, tc.registryErr, tc.settingsState, tc.breakConfigFile) c := config.New(ctx, dir, config.WithRegistry(r)) // Set up the mock Microsoft store @@ -489,9 +445,9 @@ func TestFetchMicrosoftStoreSubscription(t *testing.T) { err = c.FetchMicrosoftStoreSubscription(ctx, contracts.WithProURL(csAddr), contracts.WithMockMicrosoftStore(store)) if tc.wantErr { require.Error(t, err, "FetchMicrosoftStoreSubscription should return an error") - } else { - require.NoError(t, err, "FetchMicrosoftStoreSubscription should return no errors") + return } + require.NoError(t, err, "FetchMicrosoftStoreSubscription should return no errors") // Disable errors so we can retrieve the token r.Errors = 0 @@ -569,7 +525,7 @@ func TestUpdateRegistrySettings(t *testing.T) { _, err = db.GetDistroAndUpdateProperties(ctx, distroName, distro.Properties{}) require.NoError(t, err, "Setup: could not add dummy distro to database") - r, dir := setUpMockSettings(t, 0, tc.settingsState, false, false) + r, dir := setUpMockSettings(t, 0, tc.settingsState, false) require.NoError(t, err, "Setup: could not create empty database") c := config.New(ctx, dir, config.WithRegistry(r)) @@ -619,13 +575,12 @@ func (state settingsState) is(flag settingsState) bool { return state&flag == flag } -func setUpMockSettings(t *testing.T, mockErrors uint32, state settingsState, readOnly bool, fileBroken bool) (*registry.Mock, string) { +func setUpMockSettings(t *testing.T, mockErrors uint32, state settingsState, fileBroken bool) (*registry.Mock, string) { t.Helper() // Mock registry reg := registry.NewMock() reg.Errors = mockErrors - reg.KeyIsReadOnly = readOnly if state.is(keyExists) { reg.KeyExists = true diff --git a/windows-agent/internal/proservices/proservices_test.go b/windows-agent/internal/proservices/proservices_test.go index 235c195a4..f646d247b 100644 --- a/windows-agent/internal/proservices/proservices_test.go +++ b/windows-agent/internal/proservices/proservices_test.go @@ -24,8 +24,6 @@ func TestNew(t *testing.T) { t.Parallel() testCases := map[string]struct { - configIsReadOnly bool - breakConfig bool breakMkDir bool breakNewDistroDB bool @@ -33,7 +31,6 @@ func TestNew(t *testing.T) { wantErr bool }{ "Success when the subscription stays empty": {}, - "Success with a read-only registry": {configIsReadOnly: true}, "Success when the config cannot check if it is read-only": {breakConfig: true}, "Error when Manager cannot create its cache dir": {breakMkDir: true, wantErr: true}, @@ -50,11 +47,6 @@ func TestNew(t *testing.T) { reg := registry.NewMock() reg.KeyExists = true - if tc.configIsReadOnly { - reg.KeyExists = true - reg.KeyIsReadOnly = true - } - if tc.breakMkDir { dir = filepath.Join(dir, "proservices") err := os.WriteFile(dir, []byte("♫♪ Never gonna give you up ♫♪"), 0600) From ac3051ba87df55e44ba9956e5cc87fb5e3581d79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edu=20G=C3=B3mez=20Escandell?= Date: Mon, 30 Oct 2023 16:05:19 +0100 Subject: [PATCH 06/16] Remove write-to-registry functions from config --- windows-agent/internal/config/config.go | 7 ++----- windows-agent/internal/config/config_marshal.go | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/windows-agent/internal/config/config.go b/windows-agent/internal/config/config.go index 3b227e846..b185ed5f9 100644 --- a/windows-agent/internal/config/config.go +++ b/windows-agent/internal/config/config.go @@ -25,12 +25,9 @@ const registryPath = `Software\Canonical\UbuntuPro` // Registry abstracts away access to the windows registry. type Registry interface { - HKCUCreateKey(path string, access uint32) (newk uintptr, err error) - HKCUOpenKey(path string, access uint32) (uintptr, error) + HKCUOpenKey(path string) (uintptr, error) CloseKey(k uintptr) ReadValue(k uintptr, field string) (value string, err error) - WriteValue(k uintptr, field string, value string) (err error) - WriteMultilineValue(k uintptr, field string, value string) (err error) } // Config manages configuration parameters. It is a wrapper around a dictionary @@ -160,7 +157,7 @@ func (c *Config) set(ctx context.Context, field *string, value string) error { c.mu.Lock() defer c.mu.Unlock() - // Load before dumping to avoid overriding recent changes to registry + // Load before dumping to avoid overriding recent changes to file if err := c.load(); err != nil { return err } diff --git a/windows-agent/internal/config/config_marshal.go b/windows-agent/internal/config/config_marshal.go index 4fb857db1..804f07f36 100644 --- a/windows-agent/internal/config/config_marshal.go +++ b/windows-agent/internal/config/config_marshal.go @@ -51,7 +51,7 @@ func (h *marshalHelper) loadFile(cachePath string) (err error) { } func (h *marshalHelper) loadRegistry(reg Registry) (err error) { - k, err := reg.HKCUOpenKey(registryPath, registry.READ) + k, err := reg.HKCUOpenKey(registryPath) if errors.Is(err, registry.ErrKeyNotExist) { // Default values h.Subscription.Organization = "" From 2219d2e45b7aab6e38734e12a4a9b532d1315415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edu=20G=C3=B3mez=20Escandell?= Date: Mon, 30 Oct 2023 16:15:22 +0100 Subject: [PATCH 07/16] Remove registry non-virtualization We no longer need this, as we don't write to registry; we only read. --- msix/UbuntuProForWindows/Package.appxmanifest | 6 ------ 1 file changed, 6 deletions(-) diff --git a/msix/UbuntuProForWindows/Package.appxmanifest b/msix/UbuntuProForWindows/Package.appxmanifest index 64c172b5d..1a574d61a 100644 --- a/msix/UbuntuProForWindows/Package.appxmanifest +++ b/msix/UbuntuProForWindows/Package.appxmanifest @@ -21,14 +21,8 @@ Canonical Group Limited Images\StoreLogo.png - disabled disabled - - - HKEY_CURRENT_USER\Software\Canonical\UbuntuPro - - $(KnownFolder:LocalAppData)\Ubuntu Pro From fda34c5589f7b15e77d8ea16394bbe18bbd552cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edu=20G=C3=B3mez=20Escandell?= Date: Tue, 31 Oct 2023 13:00:53 +0100 Subject: [PATCH 08/16] UI tests: remove tests regarding a read-only registry --- .../internal/proservices/ui/ui_test.go | 43 +++++-------------- 1 file changed, 10 insertions(+), 33 deletions(-) diff --git a/windows-agent/internal/proservices/ui/ui_test.go b/windows-agent/internal/proservices/ui/ui_test.go index ca841151f..243be2b5a 100644 --- a/windows-agent/internal/proservices/ui/ui_test.go +++ b/windows-agent/internal/proservices/ui/ui_test.go @@ -130,19 +130,10 @@ func TestGetSubscriptionInfo(t *testing.T) { wantImmutable bool wantErr bool }{ - "Success with a non-subscription": {config: mockConfig{source: config.SourceNone}, wantType: none}, - "Success with a read-only non-subscription": {config: mockConfig{source: config.SourceNone, registryReadOnly: true}, wantType: none, wantImmutable: true}, - - "Success with an organization subscription": {config: mockConfig{source: config.SourceRegistry}, wantType: organization}, - "Success with a read-only organization subscription": {config: mockConfig{source: config.SourceRegistry, registryReadOnly: true}, wantType: organization, wantImmutable: true}, - - "Success with a user subscription": {config: mockConfig{source: config.SourceUser}, wantType: user}, - "Success with a read-only user subscription": {config: mockConfig{source: config.SourceUser, registryReadOnly: true}, wantType: user, wantImmutable: true}, - - "Success with a store subscription": {config: mockConfig{source: config.SourceMicrosoftStore}, wantType: store}, - "Success with a read-only store subscription": {config: mockConfig{source: config.SourceMicrosoftStore, registryReadOnly: true}, wantType: store, wantImmutable: true}, - - "Error when the read-only check fails": {config: mockConfig{isReadOnlyErr: true}, wantErr: true}, + "Success with a non-subscription": {config: mockConfig{source: config.SourceNone}, wantType: none}, + "Success with an organization subscription": {config: mockConfig{source: config.SourceRegistry}, wantType: organization}, + "Success with a user subscription": {config: mockConfig{source: config.SourceUser}, wantType: user}, + "Success with a store subscription": {config: mockConfig{source: config.SourceMicrosoftStore}, wantType: store}, "Error when the subscription cannot be retreived": {config: mockConfig{subscriptionErr: true}, wantErr: true}, } @@ -183,11 +174,10 @@ func TestNotifyPurchase(t *testing.T) { "Success with a non-subscription": {config: mockConfig{source: config.SourceNone}, wantType: store}, "Success with an existing user subscription": {config: mockConfig{source: config.SourceUser}, wantType: store}, - "Error to fetch MS Store": {config: mockConfig{source: config.SourceNone, fetchErr: true}, wantType: none, wantErr: true}, - "Error to set the subscription": {config: mockConfig{source: config.SourceNone, setSubscriptionErr: true}, wantType: none, wantErr: true}, - "Error to read the registry": {config: mockConfig{source: config.SourceNone, isReadOnlyErr: true}, wantType: none, wantErr: true}, - "Error with an existing store subscription": {config: mockConfig{source: config.SourceMicrosoftStore}, wantType: store, wantErr: true}, - "Error with a read-only organization subscription": {config: mockConfig{source: config.SourceRegistry, registryReadOnly: true}, wantType: organization, wantImmutable: true, wantErr: true}, + "Error to fetch MS Store": {config: mockConfig{source: config.SourceNone, fetchErr: true}, wantType: none, wantErr: true}, + "Error to set the subscription": {config: mockConfig{source: config.SourceNone, setSubscriptionErr: true}, wantType: none, wantErr: true}, + "Error to read the registry": {config: mockConfig{source: config.SourceNone, subscriptionErr: true}, wantType: none, wantErr: true}, + "Error with an existing store subscription": {config: mockConfig{source: config.SourceMicrosoftStore}, wantType: store, wantErr: true}, } for name, tc := range testCases { @@ -215,9 +205,7 @@ func TestNotifyPurchase(t *testing.T) { } type mockConfig struct { - registryReadOnly bool // reports registry as read only setSubscriptionErr bool // Config errors out in SetSubscription function - isReadOnlyErr bool // Config errors out in IsReadOnly function subscriptionErr bool // Config errors out in Subscription function fetchErr bool // Config errors out in FetchMicrosoftStoreSubscription function @@ -233,30 +221,19 @@ func (m *mockConfig) SetUserSubscription(ctx context.Context, token string) erro m.source = config.SourceUser return nil } -func (m mockConfig) IsReadOnly() (bool, error) { - if m.isReadOnlyErr { - return false, errors.New("IsReadOnly error") - } - return m.registryReadOnly, nil -} + func (m mockConfig) Subscription(context.Context) (string, config.Source, error) { if m.subscriptionErr { return "", config.SourceNone, errors.New("Subscription error") } return m.token, m.source, nil } + func (m *mockConfig) FetchMicrosoftStoreSubscription(ctx context.Context, args ...contracts.Option) error { if len(args) != 0 { panic("The variadic argument exists solely to match the interface. Do not use.") } - readOnly, err := m.IsReadOnly() - if err != nil { - return err - } - if readOnly { - return errors.New("FetchMicrosoftStoreSubscription found read-only registry") - } if m.fetchErr { return errors.New("FetchMicrosoftStoreSubscription error") } From f5b920ad2545de8c39dca78bf2769937523abd09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edu=20G=C3=B3mez=20Escandell?= Date: Tue, 7 Nov 2023 15:02:57 +0200 Subject: [PATCH 09/16] Removed immutable field from AgentAPI --- agentapi/agentapi.proto | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/agentapi/agentapi.proto b/agentapi/agentapi.proto index dfb562984..881bd7dc4 100644 --- a/agentapi/agentapi.proto +++ b/agentapi/agentapi.proto @@ -19,13 +19,12 @@ message ProAttachInfo { message SubscriptionInfo { string productId = 1; // The ID of the Ubuntu Pro For Windows product on the Microsoft Store. - bool immutable = 2; // True if the user is not allowed to modify the subscription. oneof subscriptionType { - Empty none = 3; // There is no active subscription. - Empty user = 4; // The subscription is managed by the user with a pro token from the GUI or the registry. - Empty organization = 5; // The subscription is managed by the sysadmin with a pro token from the registry. - Empty microsoftStore = 6; // The subscription is managed via the Microsoft store. + Empty none = 2; // There is no active subscription. + Empty user = 3; // The subscription is managed by the user with a pro token from the GUI or the registry. + Empty organization = 4; // The subscription is managed by the sysadmin with a pro token from the registry. + Empty microsoftStore = 5; // The subscription is managed via the Microsoft store. }; } From 975541326ab587f9fbdd295dd6d13f08e4f44373 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edu=20G=C3=B3mez=20Escandell?= Date: Tue, 7 Nov 2023 15:03:17 +0200 Subject: [PATCH 10/16] Re-generate GRPC bindings --- agentapi/dart/lib/src/agentapi.pb.dart | 74 +++++------- agentapi/dart/lib/src/agentapi.pbjson.dart | 20 ++-- agentapi/go/agentapi.pb.go | 128 ++++++++++----------- agentapi/go/agentapi_grpc.pb.go | 2 +- 4 files changed, 99 insertions(+), 125 deletions(-) diff --git a/agentapi/dart/lib/src/agentapi.pb.dart b/agentapi/dart/lib/src/agentapi.pb.dart index 85f60845c..539bac118 100644 --- a/agentapi/dart/lib/src/agentapi.pb.dart +++ b/agentapi/dart/lib/src/agentapi.pb.dart @@ -106,7 +106,6 @@ enum SubscriptionInfo_SubscriptionType { class SubscriptionInfo extends $pb.GeneratedMessage { factory SubscriptionInfo({ $core.String? productId, - $core.bool? immutable, Empty? none, Empty? user, Empty? organization, @@ -116,9 +115,6 @@ class SubscriptionInfo extends $pb.GeneratedMessage { if (productId != null) { $result.productId = productId; } - if (immutable != null) { - $result.immutable = immutable; - } if (none != null) { $result.none = none; } @@ -138,20 +134,19 @@ class SubscriptionInfo extends $pb.GeneratedMessage { factory SubscriptionInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); static const $core.Map<$core.int, SubscriptionInfo_SubscriptionType> _SubscriptionInfo_SubscriptionTypeByTag = { - 3 : SubscriptionInfo_SubscriptionType.none, - 4 : SubscriptionInfo_SubscriptionType.user, - 5 : SubscriptionInfo_SubscriptionType.organization, - 6 : SubscriptionInfo_SubscriptionType.microsoftStore, + 2 : SubscriptionInfo_SubscriptionType.none, + 3 : SubscriptionInfo_SubscriptionType.user, + 4 : SubscriptionInfo_SubscriptionType.organization, + 5 : SubscriptionInfo_SubscriptionType.microsoftStore, 0 : SubscriptionInfo_SubscriptionType.notSet }; static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'SubscriptionInfo', package: const $pb.PackageName(_omitMessageNames ? '' : 'agentapi'), createEmptyInstance: create) - ..oo(0, [3, 4, 5, 6]) + ..oo(0, [2, 3, 4, 5]) ..aOS(1, _omitFieldNames ? '' : 'productId', protoName: 'productId') - ..aOB(2, _omitFieldNames ? '' : 'immutable') - ..aOM(3, _omitFieldNames ? '' : 'none', subBuilder: Empty.create) - ..aOM(4, _omitFieldNames ? '' : 'user', subBuilder: Empty.create) - ..aOM(5, _omitFieldNames ? '' : 'organization', subBuilder: Empty.create) - ..aOM(6, _omitFieldNames ? '' : 'microsoftStore', protoName: 'microsoftStore', subBuilder: Empty.create) + ..aOM(2, _omitFieldNames ? '' : 'none', subBuilder: Empty.create) + ..aOM(3, _omitFieldNames ? '' : 'user', subBuilder: Empty.create) + ..aOM(4, _omitFieldNames ? '' : 'organization', subBuilder: Empty.create) + ..aOM(5, _omitFieldNames ? '' : 'microsoftStore', protoName: 'microsoftStore', subBuilder: Empty.create) ..hasRequiredFields = false ; @@ -189,57 +184,48 @@ class SubscriptionInfo extends $pb.GeneratedMessage { void clearProductId() => clearField(1); @$pb.TagNumber(2) - $core.bool get immutable => $_getBF(1); + Empty get none => $_getN(1); + @$pb.TagNumber(2) + set none(Empty v) { setField(2, v); } @$pb.TagNumber(2) - set immutable($core.bool v) { $_setBool(1, v); } + $core.bool hasNone() => $_has(1); @$pb.TagNumber(2) - $core.bool hasImmutable() => $_has(1); + void clearNone() => clearField(2); @$pb.TagNumber(2) - void clearImmutable() => clearField(2); + Empty ensureNone() => $_ensure(1); @$pb.TagNumber(3) - Empty get none => $_getN(2); + Empty get user => $_getN(2); @$pb.TagNumber(3) - set none(Empty v) { setField(3, v); } + set user(Empty v) { setField(3, v); } @$pb.TagNumber(3) - $core.bool hasNone() => $_has(2); + $core.bool hasUser() => $_has(2); @$pb.TagNumber(3) - void clearNone() => clearField(3); + void clearUser() => clearField(3); @$pb.TagNumber(3) - Empty ensureNone() => $_ensure(2); + Empty ensureUser() => $_ensure(2); @$pb.TagNumber(4) - Empty get user => $_getN(3); + Empty get organization => $_getN(3); @$pb.TagNumber(4) - set user(Empty v) { setField(4, v); } + set organization(Empty v) { setField(4, v); } @$pb.TagNumber(4) - $core.bool hasUser() => $_has(3); + $core.bool hasOrganization() => $_has(3); @$pb.TagNumber(4) - void clearUser() => clearField(4); + void clearOrganization() => clearField(4); @$pb.TagNumber(4) - Empty ensureUser() => $_ensure(3); + Empty ensureOrganization() => $_ensure(3); @$pb.TagNumber(5) - Empty get organization => $_getN(4); + Empty get microsoftStore => $_getN(4); @$pb.TagNumber(5) - set organization(Empty v) { setField(5, v); } + set microsoftStore(Empty v) { setField(5, v); } @$pb.TagNumber(5) - $core.bool hasOrganization() => $_has(4); + $core.bool hasMicrosoftStore() => $_has(4); @$pb.TagNumber(5) - void clearOrganization() => clearField(5); + void clearMicrosoftStore() => clearField(5); @$pb.TagNumber(5) - Empty ensureOrganization() => $_ensure(4); - - @$pb.TagNumber(6) - Empty get microsoftStore => $_getN(5); - @$pb.TagNumber(6) - set microsoftStore(Empty v) { setField(6, v); } - @$pb.TagNumber(6) - $core.bool hasMicrosoftStore() => $_has(5); - @$pb.TagNumber(6) - void clearMicrosoftStore() => clearField(6); - @$pb.TagNumber(6) - Empty ensureMicrosoftStore() => $_ensure(5); + Empty ensureMicrosoftStore() => $_ensure(4); } class DistroInfo extends $pb.GeneratedMessage { diff --git a/agentapi/dart/lib/src/agentapi.pbjson.dart b/agentapi/dart/lib/src/agentapi.pbjson.dart index 4e3711a00..8da45fd21 100644 --- a/agentapi/dart/lib/src/agentapi.pbjson.dart +++ b/agentapi/dart/lib/src/agentapi.pbjson.dart @@ -39,11 +39,10 @@ const SubscriptionInfo$json = { '1': 'SubscriptionInfo', '2': [ {'1': 'productId', '3': 1, '4': 1, '5': 9, '10': 'productId'}, - {'1': 'immutable', '3': 2, '4': 1, '5': 8, '10': 'immutable'}, - {'1': 'none', '3': 3, '4': 1, '5': 11, '6': '.agentapi.Empty', '9': 0, '10': 'none'}, - {'1': 'user', '3': 4, '4': 1, '5': 11, '6': '.agentapi.Empty', '9': 0, '10': 'user'}, - {'1': 'organization', '3': 5, '4': 1, '5': 11, '6': '.agentapi.Empty', '9': 0, '10': 'organization'}, - {'1': 'microsoftStore', '3': 6, '4': 1, '5': 11, '6': '.agentapi.Empty', '9': 0, '10': 'microsoftStore'}, + {'1': 'none', '3': 2, '4': 1, '5': 11, '6': '.agentapi.Empty', '9': 0, '10': 'none'}, + {'1': 'user', '3': 3, '4': 1, '5': 11, '6': '.agentapi.Empty', '9': 0, '10': 'user'}, + {'1': 'organization', '3': 4, '4': 1, '5': 11, '6': '.agentapi.Empty', '9': 0, '10': 'organization'}, + {'1': 'microsoftStore', '3': 5, '4': 1, '5': 11, '6': '.agentapi.Empty', '9': 0, '10': 'microsoftStore'}, ], '8': [ {'1': 'subscriptionType'}, @@ -52,12 +51,11 @@ const SubscriptionInfo$json = { /// Descriptor for `SubscriptionInfo`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List subscriptionInfoDescriptor = $convert.base64Decode( - 'ChBTdWJzY3JpcHRpb25JbmZvEhwKCXByb2R1Y3RJZBgBIAEoCVIJcHJvZHVjdElkEhwKCWltbX' - 'V0YWJsZRgCIAEoCFIJaW1tdXRhYmxlEiUKBG5vbmUYAyABKAsyDy5hZ2VudGFwaS5FbXB0eUgA' - 'UgRub25lEiUKBHVzZXIYBCABKAsyDy5hZ2VudGFwaS5FbXB0eUgAUgR1c2VyEjUKDG9yZ2FuaX' - 'phdGlvbhgFIAEoCzIPLmFnZW50YXBpLkVtcHR5SABSDG9yZ2FuaXphdGlvbhI5Cg5taWNyb3Nv' - 'ZnRTdG9yZRgGIAEoCzIPLmFnZW50YXBpLkVtcHR5SABSDm1pY3Jvc29mdFN0b3JlQhIKEHN1Yn' - 'NjcmlwdGlvblR5cGU='); + 'ChBTdWJzY3JpcHRpb25JbmZvEhwKCXByb2R1Y3RJZBgBIAEoCVIJcHJvZHVjdElkEiUKBG5vbm' + 'UYAiABKAsyDy5hZ2VudGFwaS5FbXB0eUgAUgRub25lEiUKBHVzZXIYAyABKAsyDy5hZ2VudGFw' + 'aS5FbXB0eUgAUgR1c2VyEjUKDG9yZ2FuaXphdGlvbhgEIAEoCzIPLmFnZW50YXBpLkVtcHR5SA' + 'BSDG9yZ2FuaXphdGlvbhI5Cg5taWNyb3NvZnRTdG9yZRgFIAEoCzIPLmFnZW50YXBpLkVtcHR5' + 'SABSDm1pY3Jvc29mdFN0b3JlQhIKEHN1YnNjcmlwdGlvblR5cGU='); @$core.Deprecated('Use distroInfoDescriptor instead') const DistroInfo$json = { diff --git a/agentapi/go/agentapi.pb.go b/agentapi/go/agentapi.pb.go index b7b21b3af..4934c1e8e 100644 --- a/agentapi/go/agentapi.pb.go +++ b/agentapi/go/agentapi.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v3.21.12 +// protoc v4.24.3 // source: agentapi.proto package agentapi @@ -110,8 +110,7 @@ type SubscriptionInfo struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ProductId string `protobuf:"bytes,1,opt,name=productId,proto3" json:"productId,omitempty"` // The ID of the Ubuntu Pro For Windows product on the Microsoft Store. - Immutable bool `protobuf:"varint,2,opt,name=immutable,proto3" json:"immutable,omitempty"` // True if the user is not allowed to modify the subscription. + ProductId string `protobuf:"bytes,1,opt,name=productId,proto3" json:"productId,omitempty"` // The ID of the Ubuntu Pro For Windows product on the Microsoft Store. // Types that are assignable to SubscriptionType: // // *SubscriptionInfo_None @@ -160,13 +159,6 @@ func (x *SubscriptionInfo) GetProductId() string { return "" } -func (x *SubscriptionInfo) GetImmutable() bool { - if x != nil { - return x.Immutable - } - return false -} - func (m *SubscriptionInfo) GetSubscriptionType() isSubscriptionInfo_SubscriptionType { if m != nil { return m.SubscriptionType @@ -207,19 +199,19 @@ type isSubscriptionInfo_SubscriptionType interface { } type SubscriptionInfo_None struct { - None *Empty `protobuf:"bytes,3,opt,name=none,proto3,oneof"` // There is no active subscription. + None *Empty `protobuf:"bytes,2,opt,name=none,proto3,oneof"` // There is no active subscription. } type SubscriptionInfo_User struct { - User *Empty `protobuf:"bytes,4,opt,name=user,proto3,oneof"` // The subscription is managed by the user with a pro token from the GUI or the registry. + User *Empty `protobuf:"bytes,3,opt,name=user,proto3,oneof"` // The subscription is managed by the user with a pro token from the GUI or the registry. } type SubscriptionInfo_Organization struct { - Organization *Empty `protobuf:"bytes,5,opt,name=organization,proto3,oneof"` // The subscription is managed by the sysadmin with a pro token from the registry. + Organization *Empty `protobuf:"bytes,4,opt,name=organization,proto3,oneof"` // The subscription is managed by the sysadmin with a pro token from the registry. } type SubscriptionInfo_MicrosoftStore struct { - MicrosoftStore *Empty `protobuf:"bytes,6,opt,name=microsoftStore,proto3,oneof"` // The subscription is managed via the Microsoft store. + MicrosoftStore *Empty `protobuf:"bytes,5,opt,name=microsoftStore,proto3,oneof"` // The subscription is managed via the Microsoft store. } func (*SubscriptionInfo_None) isSubscriptionInfo_SubscriptionType() {} @@ -371,63 +363,61 @@ var file_agentapi_proto_rawDesc = []byte{ 0x12, 0x08, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x61, 0x70, 0x69, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x25, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0xa2, 0x02, 0x0a, 0x10, 0x53, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x84, 0x02, 0x0a, 0x10, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x49, 0x64, 0x12, 0x1c, 0x0a, - 0x09, 0x69, 0x6d, 0x6d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x09, 0x69, 0x6d, 0x6d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x25, 0x0a, 0x04, 0x6e, - 0x6f, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x67, 0x65, 0x6e, - 0x74, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x6f, - 0x6e, 0x65, 0x12, 0x25, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0f, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x48, 0x00, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x35, 0x0a, 0x0c, 0x6f, 0x72, 0x67, - 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x0f, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x48, 0x00, 0x52, 0x0c, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x39, 0x0a, 0x0e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x53, 0x74, 0x6f, - 0x72, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, - 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x48, 0x00, 0x52, 0x0e, 0x6d, 0x69, 0x63, - 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x12, 0x0a, 0x10, 0x73, - 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x22, - 0xb6, 0x01, 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x74, 0x72, 0x6f, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x19, - 0x0a, 0x08, 0x77, 0x73, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x77, 0x73, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x65, 0x74, - 0x74, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, - 0x72, 0x65, 0x74, 0x74, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, - 0x5f, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0b, 0x70, 0x72, 0x6f, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, - 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x1a, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, - 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, - 0x70, 0x6f, 0x72, 0x74, 0x32, 0xf4, 0x01, 0x0a, 0x02, 0x55, 0x49, 0x12, 0x3b, 0x0a, 0x0d, 0x41, - 0x70, 0x70, 0x6c, 0x79, 0x50, 0x72, 0x6f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x17, 0x2e, 0x61, - 0x67, 0x65, 0x6e, 0x74, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x72, 0x6f, 0x41, 0x74, 0x74, 0x61, 0x63, - 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x1a, 0x0f, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x61, 0x70, 0x69, - 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x2a, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, - 0x12, 0x0f, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x1a, 0x0f, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0f, 0x2e, 0x61, 0x67, - 0x65, 0x6e, 0x74, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1a, 0x2e, 0x61, - 0x67, 0x65, 0x6e, 0x74, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x0e, 0x4e, 0x6f, - 0x74, 0x69, 0x66, 0x79, 0x50, 0x75, 0x72, 0x63, 0x68, 0x61, 0x73, 0x65, 0x12, 0x0f, 0x2e, 0x61, - 0x67, 0x65, 0x6e, 0x74, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1a, 0x2e, - 0x61, 0x67, 0x65, 0x6e, 0x74, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x00, 0x32, 0x46, 0x0a, 0x0b, 0x57, - 0x53, 0x4c, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x37, 0x0a, 0x09, 0x43, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x14, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x61, - 0x70, 0x69, 0x2e, 0x44, 0x69, 0x73, 0x74, 0x72, 0x6f, 0x49, 0x6e, 0x66, 0x6f, 0x1a, 0x0e, 0x2e, - 0x61, 0x67, 0x65, 0x6e, 0x74, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x22, 0x00, 0x28, - 0x01, 0x30, 0x01, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x2f, 0x75, 0x62, 0x75, 0x6e, - 0x74, 0x75, 0x2d, 0x70, 0x72, 0x6f, 0x2d, 0x66, 0x6f, 0x72, 0x2d, 0x77, 0x69, 0x6e, 0x64, 0x6f, - 0x77, 0x73, 0x2f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x49, 0x64, 0x12, 0x25, 0x0a, + 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x67, + 0x65, 0x6e, 0x74, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x48, 0x00, 0x52, 0x04, + 0x6e, 0x6f, 0x6e, 0x65, 0x12, 0x25, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x48, 0x00, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x35, 0x0a, 0x0c, 0x6f, + 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x48, 0x00, 0x52, 0x0c, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x0e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x53, + 0x74, 0x6f, 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x67, 0x65, + 0x6e, 0x74, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x48, 0x00, 0x52, 0x0e, 0x6d, + 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x12, 0x0a, + 0x10, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, + 0x65, 0x22, 0xb6, 0x01, 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x74, 0x72, 0x6f, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x19, 0x0a, 0x08, 0x77, 0x73, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x77, 0x73, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, + 0x65, 0x74, 0x74, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x70, 0x72, 0x65, 0x74, 0x74, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x70, + 0x72, 0x6f, 0x5f, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x12, 0x1a, + 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x1a, 0x0a, 0x04, 0x50, 0x6f, + 0x72, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x32, 0xf4, 0x01, 0x0a, 0x02, 0x55, 0x49, 0x12, 0x3b, 0x0a, + 0x0d, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x50, 0x72, 0x6f, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x17, + 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x72, 0x6f, 0x41, 0x74, 0x74, + 0x61, 0x63, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x1a, 0x0f, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x61, + 0x70, 0x69, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x2a, 0x0a, 0x04, 0x50, 0x69, + 0x6e, 0x67, 0x12, 0x0f, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x1a, 0x0f, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x61, 0x70, 0x69, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0f, 0x2e, + 0x61, 0x67, 0x65, 0x6e, 0x74, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1a, + 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x0e, + 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x50, 0x75, 0x72, 0x63, 0x68, 0x61, 0x73, 0x65, 0x12, 0x0f, + 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, + 0x1a, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x00, 0x32, 0x46, 0x0a, + 0x0b, 0x57, 0x53, 0x4c, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x37, 0x0a, 0x09, + 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x14, 0x2e, 0x61, 0x67, 0x65, 0x6e, + 0x74, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x69, 0x73, 0x74, 0x72, 0x6f, 0x49, 0x6e, 0x66, 0x6f, 0x1a, + 0x0e, 0x2e, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x22, + 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x2f, 0x75, 0x62, + 0x75, 0x6e, 0x74, 0x75, 0x2d, 0x70, 0x72, 0x6f, 0x2d, 0x66, 0x6f, 0x72, 0x2d, 0x77, 0x69, 0x6e, + 0x64, 0x6f, 0x77, 0x73, 0x2f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/agentapi/go/agentapi_grpc.pb.go b/agentapi/go/agentapi_grpc.pb.go index ded421e62..461e8e0fc 100644 --- a/agentapi/go/agentapi_grpc.pb.go +++ b/agentapi/go/agentapi_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.3.0 -// - protoc v3.21.12 +// - protoc v4.24.3 // source: agentapi.proto package agentapi From 9c5e0074c8ff3a6454f8142ae14568eceabaea4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edu=20G=C3=B3mez=20Escandell?= Date: Tue, 7 Nov 2023 15:06:13 +0200 Subject: [PATCH 11/16] Remove calls to info.GetImmutable This field no longer exists --- windows-agent/internal/proservices/ui/ui_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/windows-agent/internal/proservices/ui/ui_test.go b/windows-agent/internal/proservices/ui/ui_test.go index 243be2b5a..d7b042ba1 100644 --- a/windows-agent/internal/proservices/ui/ui_test.go +++ b/windows-agent/internal/proservices/ui/ui_test.go @@ -156,7 +156,6 @@ func TestGetSubscriptionInfo(t *testing.T) { require.NoError(t, err, "GetSubscriptionInfo should return no errors") require.IsType(t, tc.wantType, info.GetSubscriptionType(), "Mismatched subscription types") - require.Equal(t, tc.wantImmutable, info.GetImmutable(), "Mismatched value for ReadOnly") }) } } @@ -199,7 +198,6 @@ func TestNotifyPurchase(t *testing.T) { require.NoError(t, err, "NotifyPurchase should return no errors") require.IsType(t, tc.wantType, info.GetSubscriptionType(), "Mismatched subscription types") - require.Equal(t, tc.wantImmutable, info.GetImmutable(), "Mismatched value for ReadOnly") }) } } From df7994d8f4c588c2ac684b2b66c23cdfa5452c05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edu=20G=C3=B3mez=20Escandell?= Date: Tue, 7 Nov 2023 15:09:07 +0200 Subject: [PATCH 12/16] Remove references to info.immutable This field no longer exists. The test checking this field was also removed. --- .../subscription_status_model.dart | 25 ++++++++----------- .../test/core/agent_api_client_test.dart | 3 --- .../subscription_status_model_test.dart | 10 -------- .../subscription_status_page_test.dart | 4 --- 4 files changed, 11 insertions(+), 31 deletions(-) diff --git a/gui/packages/ubuntupro/lib/pages/subscription_status/subscription_status_model.dart b/gui/packages/ubuntupro/lib/pages/subscription_status/subscription_status_model.dart index 616d0f940..8e5fc110f 100644 --- a/gui/packages/ubuntupro/lib/pages/subscription_status/subscription_status_model.dart +++ b/gui/packages/ubuntupro/lib/pages/subscription_status/subscription_status_model.dart @@ -15,21 +15,18 @@ sealed class SubscriptionStatusModel { SubscriptionInfo info, AgentApiClient client, ) { - if (!info.immutable) { - switch (info.whichSubscriptionType()) { - case SubscriptionType.organization: - return OrgSubscriptionStatusModel(); - case SubscriptionType.user: - return UserSubscriptionStatusModel(client); - case SubscriptionType.microsoftStore: - return StoreSubscriptionStatusModel(info.productId); - case SubscriptionType.none: - return SubscribeNowModel(client); - case SubscriptionType.notSet: - throw UnimplementedError('Unknown subscription type'); - } + switch (info.whichSubscriptionType()) { + case SubscriptionType.organization: + return OrgSubscriptionStatusModel(); + case SubscriptionType.user: + return UserSubscriptionStatusModel(client); + case SubscriptionType.microsoftStore: + return StoreSubscriptionStatusModel(info.productId); + case SubscriptionType.none: + return SubscribeNowModel(client); + case SubscriptionType.notSet: + throw UnimplementedError('Unknown subscription type'); } - return OrgSubscriptionStatusModel(); } SubscriptionStatusModel._(); } diff --git a/gui/packages/ubuntupro/test/core/agent_api_client_test.dart b/gui/packages/ubuntupro/test/core/agent_api_client_test.dart index 6734b9093..b363a24f2 100644 --- a/gui/packages/ubuntupro/test/core/agent_api_client_test.dart +++ b/gui/packages/ubuntupro/test/core/agent_api_client_test.dart @@ -21,7 +21,6 @@ void main() { test('no subscription info', () async { final info = await client.subscriptionInfo(); expect(info.productId, isEmpty); - expect(info.immutable, isFalse); expect(info.whichSubscriptionType(), SubscriptionType.none); }); test('pro attach user subscription', () async { @@ -29,7 +28,6 @@ void main() { final info = await client.subscriptionInfo(); expect(info.productId, isEmpty); - expect(info.immutable, isFalse); expect(info.whichSubscriptionType(), SubscriptionType.user); }); @@ -38,7 +36,6 @@ void main() { final info = await client.subscriptionInfo(); expect(info.productId, isEmpty); - expect(info.immutable, isFalse); expect(info.whichSubscriptionType(), SubscriptionType.none); }); }); diff --git a/gui/packages/ubuntupro/test/pages/subscription_status/subscription_status_model_test.dart b/gui/packages/ubuntupro/test/pages/subscription_status/subscription_status_model_test.dart index af164fa7c..ba8fb85e2 100644 --- a/gui/packages/ubuntupro/test/pages/subscription_status/subscription_status_model_test.dart +++ b/gui/packages/ubuntupro/test/pages/subscription_status/subscription_status_model_test.dart @@ -19,15 +19,8 @@ void main() { final info = SubscriptionInfo(); info.productId = 'my prod ID'; - test('immutable is org', () async { - info.immutable = true; - final model = SubscriptionStatusModel(info, client); - expect(model.runtimeType, OrgSubscriptionStatusModel); - }); - test('none subscribes now', () async { info.ensureNone(); - info.immutable = false; final model = SubscriptionStatusModel(info, client); expect(model.runtimeType, SubscribeNowModel); }); @@ -42,7 +35,6 @@ void main() { }); test('store', () async { info.ensureMicrosoftStore(); - info.immutable = false; final model = SubscriptionStatusModel(info, client); expect(model.runtimeType, StoreSubscriptionStatusModel); @@ -50,7 +42,6 @@ void main() { test('user', () async { info.ensureUser(); - info.immutable = false; final model = SubscriptionStatusModel(info, client); expect(model.runtimeType, UserSubscriptionStatusModel); @@ -58,7 +49,6 @@ void main() { test('organization', () async { info.ensureOrganization(); - info.immutable = false; final model = SubscriptionStatusModel(info, client); expect(model.runtimeType, OrgSubscriptionStatusModel); diff --git a/gui/packages/ubuntupro/test/pages/subscription_status/subscription_status_page_test.dart b/gui/packages/ubuntupro/test/pages/subscription_status/subscription_status_page_test.dart index f13d87352..0c7f4b1d0 100644 --- a/gui/packages/ubuntupro/test/pages/subscription_status/subscription_status_page_test.dart +++ b/gui/packages/ubuntupro/test/pages/subscription_status/subscription_status_page_test.dart @@ -14,7 +14,6 @@ void main() { final info = SubscriptionInfo(); testWidgets('user', (tester) async { info.ensureUser(); - info.immutable = false; final app = buildApp(info, client); await tester.pumpWidget(app); @@ -27,7 +26,6 @@ void main() { testWidgets('store', (tester) async { info.ensureMicrosoftStore(); - info.immutable = false; final app = buildApp(info, client); await tester.pumpWidget(app); @@ -40,7 +38,6 @@ void main() { testWidgets('organization', (tester) async { info.ensureOrganization(); - info.immutable = false; final app = buildApp(info, client); await tester.pumpWidget(app); @@ -55,7 +52,6 @@ void main() { final mockClient = FakeAgentApiClient(); final info = ValueNotifier(SubscriptionInfo()); info.value.ensureUser(); - info.value.immutable = false; registerServiceInstance(mockClient); final app = ChangeNotifierProvider.value( value: info, From f3c264e93195294bbec0863b2655f5334430f29b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edu=20G=C3=B3mez=20Escandell?= Date: Tue, 7 Nov 2023 15:12:08 +0200 Subject: [PATCH 13/16] Prioritize Organization settings Now that the write permissions to the registry are irrelevant, we need a way for the sysadmin to enforce its configuration on the machine. To achieve this, we set the org-provided Landscape and Pro settings as the top layer. --- windows-agent/internal/config/config_source.go | 16 ++++++++-------- windows-agent/internal/config/config_test.go | 9 +++++---- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/windows-agent/internal/config/config_source.go b/windows-agent/internal/config/config_source.go index 17225474f..940fdafa1 100644 --- a/windows-agent/internal/config/config_source.go +++ b/windows-agent/internal/config/config_source.go @@ -26,6 +26,10 @@ type subscription struct { } func (s subscription) resolve() (string, Source) { + if s.Organization != "" { + return s.Organization, SourceRegistry + } + if s.Store != "" { return s.Store, SourceMicrosoftStore } @@ -34,10 +38,6 @@ func (s subscription) resolve() (string, Source) { return s.User, SourceUser } - if s.Organization != "" { - return s.Organization, SourceRegistry - } - return "", SourceNone } @@ -50,13 +50,13 @@ type landscapeConf struct { } func (p landscapeConf) resolve() (string, Source) { - if p.UserConfig != "" { - return p.UserConfig, SourceUser - } - if p.OrgConfig != "" { return p.OrgConfig, SourceRegistry } + if p.UserConfig != "" { + return p.UserConfig, SourceUser + } + return "", SourceNone } diff --git a/windows-agent/internal/config/config_test.go b/windows-agent/internal/config/config_test.go index 015e84d29..ae1aaaffc 100644 --- a/windows-agent/internal/config/config_test.go +++ b/windows-agent/internal/config/config_test.go @@ -75,9 +75,10 @@ func TestSubscription(t *testing.T) { "Success when there is a user token": {settingsState: userTokenHasValue, wantToken: "user_token", wantSource: config.SourceUser}, "Success when there is a store token": {settingsState: storeTokenHasValue, wantToken: "store_token", wantSource: config.SourceMicrosoftStore}, - "Success when there are organization and user tokens": {settingsState: orgTokenHasValue | userTokenHasValue, wantToken: "user_token", wantSource: config.SourceUser}, - "Success when there are organization and store tokens": {settingsState: orgTokenHasValue | storeTokenHasValue, wantToken: "store_token", wantSource: config.SourceMicrosoftStore}, - "Success when there are organization and user tokens, and an empty store token": {settingsState: orgTokenHasValue | userTokenHasValue | storeTokenExists, wantToken: "user_token", wantSource: config.SourceUser}, + "Success when there are organization and user tokens": {settingsState: orgTokenHasValue | userTokenHasValue, wantToken: "org_token", wantSource: config.SourceRegistry}, + "Success when there are organization and store tokens": {settingsState: orgTokenHasValue | storeTokenHasValue, wantToken: "org_token", wantSource: config.SourceRegistry}, + "Success when there are store and user tokens": {settingsState: userTokenHasValue | storeTokenHasValue, wantToken: "store_token", wantSource: config.SourceMicrosoftStore}, + "Success when there are organization and user tokens, and an empty store token": {settingsState: orgTokenHasValue | userTokenHasValue | storeTokenExists, wantToken: "org_token", wantSource: config.SourceRegistry}, "Error when the registry key cannot be opened": {settingsState: orgTokenHasValue, mockErrors: registry.MockErrOnOpenKey, wantError: true}, "Error when the registry key cannot be read from": {settingsState: orgTokenHasValue, mockErrors: registry.MockErrReadValue, wantError: true}, @@ -129,7 +130,7 @@ func TestLandscapeConfig(t *testing.T) { "Success when there is an organization conf": {settingsState: orgLandscapeConfigHasValue, wantLandscapeConfig: "[client]\nuser=BigOrg", wantSource: config.SourceRegistry}, "Success when there is a user conf": {settingsState: userLandscapeConfigHasValue, wantLandscapeConfig: "[client]\nuser=JohnDoe", wantSource: config.SourceUser}, - "Success when there are organization and user confs": {settingsState: orgLandscapeConfigHasValue | userLandscapeConfigHasValue, wantLandscapeConfig: "[client]\nuser=JohnDoe", wantSource: config.SourceUser}, + "Success when there are organization and user confs": {settingsState: orgLandscapeConfigHasValue | userLandscapeConfigHasValue, wantLandscapeConfig: "[client]\nuser=BigOrg", wantSource: config.SourceRegistry}, "Error when the registry key cannot be opened": {settingsState: orgTokenHasValue, mockErrors: registry.MockErrOnOpenKey, wantError: true}, "Error when the registry key cannot be read from": {settingsState: orgTokenHasValue, mockErrors: registry.MockErrReadValue, wantError: true}, From 280430ecd414a460dfd98c3a6e4f2dac180cd7a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edu=20G=C3=B3mez=20Escandell?= Date: Wed, 8 Nov 2023 09:52:22 +0200 Subject: [PATCH 14/16] =?UTF-8?q?=F0=9F=8C=B1=20Improve=20E2E=20error=20re?= =?UTF-8?q?porting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- end-to-end/utils_test.go | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/end-to-end/utils_test.go b/end-to-end/utils_test.go index c493711db..5e8373c0b 100644 --- a/end-to-end/utils_test.go +++ b/end-to-end/utils_test.go @@ -154,14 +154,26 @@ func stopAgent(ctx context.Context) error { func distroIsProAttached(t *testing.T, ctx context.Context, d gowsl.Distro) (bool, error) { t.Helper() - out, err := d.Command(ctx, "pro status --format=json").Output() - if err != nil { - return false, fmt.Errorf("could not call pro status: %v. %s", err, out) + var stdout, stderr bytes.Buffer + + cmd := d.Command(ctx, "pro status --format=json") + + // We need separate Stdout and Stderr. We cannot combine them because the + // pro client prints warnings to Stderr, which makes the combined output + // invalid JSON. We also cannot ignore either: + // - We need StdOut to parse the JSON output. + // - We need Stderr to read the error message in case the command fails. + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + return false, fmt.Errorf("could not call pro status: %v.\nSTDOUT: %s\nSTDERR: %s", err, &stdout, &stderr) } var response struct { Attached bool } + out := stdout.Bytes() if err := json.Unmarshal(out, &response); err != nil { return false, fmt.Errorf("could not parse pro status response: %v: %s", err, out) } From a8545b86e885ceeca72940500031b700e8578d28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edu=20G=C3=B3mez=20Escandell?= Date: Tue, 14 Nov 2023 10:30:56 +0100 Subject: [PATCH 15/16] =?UTF-8?q?=F0=9F=8C=B1=20Fix=20typo=20in=20ui=5Ftes?= =?UTF-8?q?t.go?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- windows-agent/internal/proservices/ui/ui_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows-agent/internal/proservices/ui/ui_test.go b/windows-agent/internal/proservices/ui/ui_test.go index d7b042ba1..5d6cfe2bc 100644 --- a/windows-agent/internal/proservices/ui/ui_test.go +++ b/windows-agent/internal/proservices/ui/ui_test.go @@ -134,7 +134,7 @@ func TestGetSubscriptionInfo(t *testing.T) { "Success with an organization subscription": {config: mockConfig{source: config.SourceRegistry}, wantType: organization}, "Success with a user subscription": {config: mockConfig{source: config.SourceUser}, wantType: user}, "Success with a store subscription": {config: mockConfig{source: config.SourceMicrosoftStore}, wantType: store}, - "Error when the subscription cannot be retreived": {config: mockConfig{subscriptionErr: true}, wantErr: true}, + "Error when the subscription cannot be retrieved": {config: mockConfig{subscriptionErr: true}, wantErr: true}, } for name, tc := range testCases { From f6f239a939bedc736ada057a791bd2a59bdfaf2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edu=20G=C3=B3mez=20Escandell?= Date: Thu, 16 Nov 2023 14:49:54 +0100 Subject: [PATCH 16/16] Improved test names where there is layering --- windows-agent/internal/config/config_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/windows-agent/internal/config/config_test.go b/windows-agent/internal/config/config_test.go index ae1aaaffc..6aa353c1e 100644 --- a/windows-agent/internal/config/config_test.go +++ b/windows-agent/internal/config/config_test.go @@ -75,10 +75,10 @@ func TestSubscription(t *testing.T) { "Success when there is a user token": {settingsState: userTokenHasValue, wantToken: "user_token", wantSource: config.SourceUser}, "Success when there is a store token": {settingsState: storeTokenHasValue, wantToken: "store_token", wantSource: config.SourceMicrosoftStore}, - "Success when there are organization and user tokens": {settingsState: orgTokenHasValue | userTokenHasValue, wantToken: "org_token", wantSource: config.SourceRegistry}, - "Success when there are organization and store tokens": {settingsState: orgTokenHasValue | storeTokenHasValue, wantToken: "org_token", wantSource: config.SourceRegistry}, - "Success when there are store and user tokens": {settingsState: userTokenHasValue | storeTokenHasValue, wantToken: "store_token", wantSource: config.SourceMicrosoftStore}, - "Success when there are organization and user tokens, and an empty store token": {settingsState: orgTokenHasValue | userTokenHasValue | storeTokenExists, wantToken: "org_token", wantSource: config.SourceRegistry}, + "Success when an organization token shadows a user token": {settingsState: orgTokenHasValue | userTokenHasValue, wantToken: "org_token", wantSource: config.SourceRegistry}, + "Success when an organization token shadows a store token": {settingsState: orgTokenHasValue | storeTokenHasValue, wantToken: "org_token", wantSource: config.SourceRegistry}, + "Success when a store token shadows a user token": {settingsState: userTokenHasValue | storeTokenHasValue, wantToken: "store_token", wantSource: config.SourceMicrosoftStore}, + "Success when an organization token shadows a user token, and an empty store token": {settingsState: orgTokenHasValue | userTokenHasValue | storeTokenExists, wantToken: "org_token", wantSource: config.SourceRegistry}, "Error when the registry key cannot be opened": {settingsState: orgTokenHasValue, mockErrors: registry.MockErrOnOpenKey, wantError: true}, "Error when the registry key cannot be read from": {settingsState: orgTokenHasValue, mockErrors: registry.MockErrReadValue, wantError: true}, @@ -130,7 +130,7 @@ func TestLandscapeConfig(t *testing.T) { "Success when there is an organization conf": {settingsState: orgLandscapeConfigHasValue, wantLandscapeConfig: "[client]\nuser=BigOrg", wantSource: config.SourceRegistry}, "Success when there is a user conf": {settingsState: userLandscapeConfigHasValue, wantLandscapeConfig: "[client]\nuser=JohnDoe", wantSource: config.SourceUser}, - "Success when there are organization and user confs": {settingsState: orgLandscapeConfigHasValue | userLandscapeConfigHasValue, wantLandscapeConfig: "[client]\nuser=BigOrg", wantSource: config.SourceRegistry}, + "Success when an organization config shadows a user config": {settingsState: orgLandscapeConfigHasValue | userLandscapeConfigHasValue, wantLandscapeConfig: "[client]\nuser=BigOrg", wantSource: config.SourceRegistry}, "Error when the registry key cannot be opened": {settingsState: orgTokenHasValue, mockErrors: registry.MockErrOnOpenKey, wantError: true}, "Error when the registry key cannot be read from": {settingsState: orgTokenHasValue, mockErrors: registry.MockErrReadValue, wantError: true},