Skip to content

Commit 9e44af8

Browse files
parfenovvsCopilot
andauthored
feat: remove license key activation (#59)
* feat: remove license key activation * Update cmd/login.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent bcfd774 commit 9e44af8

11 files changed

Lines changed: 52 additions & 346 deletions

File tree

cmd/login.go

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
/*
2-
Copyright © 2025 NAME HERE <EMAIL ADDRESS>
3-
*/
41
package cmd
52

63
import (
@@ -15,24 +12,14 @@ func NewLoginCommand(sm session.SessionManager) *cobra.Command {
1512
Long: `Uses provided credentials to perform activation for this device.
1613
The command claims available seat of your Malwarebytes license.`,
1714
Run: func(cmd *cobra.Command, args []string) {
18-
var err error
19-
20-
key, _ := cmd.Flags().GetString("licenseKey")
21-
if key == "" {
22-
code, _ := cmd.Flags().GetString("mbCode")
23-
err = sm.LoginWithCode(code)
24-
} else {
25-
err = sm.LoginWithKey(key)
26-
}
27-
28-
// Handle any errors that might have occurred
15+
code, _ := cmd.Flags().GetString("code")
16+
err := sm.LoginWithCode(code)
2917
HandleError(err)
3018
},
3119
}
3220

33-
cmd.Flags().StringP("licenseKey", "k", "", "License key.")
34-
cmd.Flags().StringP("mbCode", "c", "", "MB-code.")
35-
cmd.MarkFlagsOneRequired("licenseKey", "mbCode")
21+
cmd.Flags().StringP("code", "c", "", "MB-code.")
22+
cmd.MarkFlagRequired("code")
3623

3724
return cmd
3825
}

cmd/login_test.go

Lines changed: 14 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -6,95 +6,59 @@ import (
66

77
func TestNewLoginCommand(t *testing.T) {
88
sm := &mockSessionManager{}
9-
9+
1010
cmd := NewLoginCommand(sm)
11-
11+
1212
if cmd.Use != "login" {
1313
t.Errorf("Expected Use to be 'login', got '%s'", cmd.Use)
1414
}
15-
15+
1616
if cmd.Short != "Activates this device with your Malwarebytes license." {
1717
t.Errorf("Expected Short to be 'Activates this device with your Malwarebytes license.', got '%s'", cmd.Short)
1818
}
19-
19+
2020
expectedLong := `Uses provided credentials to perform activation for this device.
2121
The command claims available seat of your Malwarebytes license.`
2222
if cmd.Long != expectedLong {
2323
t.Errorf("Expected Long to be '%s', got '%s'", expectedLong, cmd.Long)
2424
}
25-
25+
2626
if cmd.Run == nil {
2727
t.Error("Expected Run function to be defined")
2828
}
2929
}
3030

3131
func TestLoginCommandFlags(t *testing.T) {
3232
sm := &mockSessionManager{}
33-
3433
cmd := NewLoginCommand(sm)
35-
36-
// Test licenseKey flag
37-
licenseKeyFlag := cmd.Flags().Lookup("licenseKey")
38-
if licenseKeyFlag == nil {
39-
t.Error("Expected licenseKey flag to be defined")
40-
} else {
41-
if licenseKeyFlag.Shorthand != "k" {
42-
t.Errorf("Expected licenseKey shorthand to be 'k', got '%s'", licenseKeyFlag.Shorthand)
43-
}
44-
if licenseKeyFlag.Usage != "License key." {
45-
t.Errorf("Expected licenseKey usage to be 'License key.', got '%s'", licenseKeyFlag.Usage)
46-
}
47-
}
48-
49-
// Test mbCode flag
50-
mbCodeFlag := cmd.Flags().Lookup("mbCode")
34+
mbCodeFlag := cmd.Flags().Lookup("code")
5135
if mbCodeFlag == nil {
52-
t.Error("Expected mbCode flag to be defined")
36+
t.Error("Expected code flag to be defined")
5337
} else {
5438
if mbCodeFlag.Shorthand != "c" {
55-
t.Errorf("Expected mbCode shorthand to be 'c', got '%s'", mbCodeFlag.Shorthand)
39+
t.Errorf("Expected code shorthand to be 'c', got '%s'", mbCodeFlag.Shorthand)
5640
}
5741
if mbCodeFlag.Usage != "MB-code." {
58-
t.Errorf("Expected mbCode usage to be 'MB-code.', got '%s'", mbCodeFlag.Usage)
42+
t.Errorf("Expected code usage to be 'MB-code.', got '%s'", mbCodeFlag.Usage)
5943
}
6044
}
6145
}
6246

63-
func TestLoginCommandWithLicenseKey(t *testing.T) {
64-
sm := &mockSessionManager{}
65-
66-
cmd := NewLoginCommand(sm)
67-
cmd.Flags().Set("licenseKey", "test-license-key")
68-
cmd.Run(cmd, []string{})
69-
70-
// Verify that LoginWithKey was called with the correct parameter
71-
// Note: We can't directly verify this with our simple mock, but we can test the logic
72-
}
73-
7447
func TestLoginCommandWithMBCode(t *testing.T) {
7548
sm := &mockSessionManager{}
76-
49+
7750
cmd := NewLoginCommand(sm)
78-
cmd.Flags().Set("mbCode", "test-mb-code")
51+
cmd.Flags().Set("code", "test-mb-code")
7952
cmd.Run(cmd, []string{})
80-
53+
8154
// Verify that LoginWithCode was called with the correct parameter
8255
// Note: We can't directly verify this with our simple mock, but we can test the logic
8356
}
8457

8558
func TestLoginCommandLogic(t *testing.T) {
86-
// Test the logic without running the command to avoid HandleError os.Exit
8759
sm := &mockSessionManager{}
88-
89-
// Test LoginWithKey
90-
err := sm.LoginWithKey("test-license-key")
91-
if err != nil {
92-
t.Errorf("Expected no error from sm.LoginWithKey, got %v", err)
93-
}
94-
95-
// Test LoginWithCode
96-
err = sm.LoginWithCode("test-mb-code")
60+
err := sm.LoginWithCode("test-mb-code")
9761
if err != nil {
9862
t.Errorf("Expected no error from sm.LoginWithCode, got %v", err)
9963
}
100-
}
64+
}

pkg/remote/holocron.go

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ var client = &http.Client{Timeout: time.Second * 10}
2525

2626
type Holocron interface {
2727
RegisterDevice() (string, error)
28-
ActivateDevice(installationToken string, key string, mbcode bool) (*DeviceModule, error)
28+
ActivateDevice(installationToken string, code string) (*DeviceModule, error)
2929
CheckDevice(installationToken string) (*DeviceModule, error)
3030
DeactivateDevice(installationToken string) (*DeviceModule, error)
3131
VpnRegisterPublicKey(installationToken string, key string) (*VpnIpAddresses, error)
@@ -81,18 +81,13 @@ func (api *DefaultHolocron) RegisterDevice() (string, error) {
8181
return response.Data.RegisterDevice.Device.InstallationToken, nil
8282
}
8383

84-
func (api *DefaultHolocron) ActivateDevice(installationToken string, key string, mbcode bool) (*DeviceModule, error) {
84+
func (api *DefaultHolocron) ActivateDevice(installationToken string, code string) (*DeviceModule, error) {
8585
log.Debugln("Begin: `ActivateDevice` request")
8686
input := ActivateDeviceInput{
87-
Modules: []ProductModule{ProductModulePrivacy},
88-
ActivationMode: ActivationModePassive,
89-
}
90-
if mbcode {
91-
input.ActivationMethod = ActivationMethodOneTimeToken
92-
input.OneTimeToken = key
93-
} else {
94-
input.ActivationMethod = ActivationMethodLicenseKey
95-
input.LicenseKey = key
87+
Modules: []ProductModule{ProductModulePrivacy},
88+
ActivationMode: ActivationModePassive,
89+
ActivationMethod: ActivationMethodOneTimeToken,
90+
OneTimeToken: code,
9691
}
9792

9893
requestBody := map[string]interface{}{

pkg/remote/holocron_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ func TestApiSuccessCases(t *testing.T) {
7272
name: "ActivateDevice",
7373
response: `{"data": {"activateDevice": {"deviceModules": {"privacy": {"status": "licensed", "termEndsOn": "2025-12-31"}}}}}`,
7474
testFunc: func(api *DefaultHolocron) (interface{}, error) {
75-
return api.ActivateDevice("mock-token", "mock-key", true)
75+
return api.ActivateDevice("mock-token", "mock-code")
7676
},
7777
validate: func(t *testing.T, result interface{}) {
7878
deviceModule := result.(*DeviceModule)
@@ -207,7 +207,7 @@ func TestApiErrorCases(t *testing.T) {
207207
name: "ActivateDevice",
208208
response: `{ "errors": [ { "message": "mock-error" } ] }`,
209209
testFunc: func(api *DefaultHolocron) (interface{}, error) {
210-
return api.ActivateDevice("", "", false)
210+
return api.ActivateDevice("", "")
211211
},
212212
},
213213
{

pkg/remote/schema.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ type ActivationMethod string
1818

1919
const (
2020
ActivationMethodOneTimeToken ActivationMethod = "oneTimeToken"
21-
ActivationMethodLicenseKey ActivationMethod = "licenseKey"
2221
)
2322

2423
type ActivationMode string
@@ -52,7 +51,6 @@ type ActivateDeviceInput struct {
5251
ActivationMethod ActivationMethod `json:"activationMethod"`
5352
ActivationMode ActivationMode `json:"activationMode"`
5453
OneTimeToken string `json:"oneTimeToken"`
55-
LicenseKey string `json:"licenseKey"`
5654
}
5755

5856
type HolocronResponse[D any] struct {

pkg/remote/schema_test.go

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@ func TestActivationMethod_Constants(t *testing.T) {
1515
if ActivationMethodOneTimeToken != "oneTimeToken" {
1616
t.Errorf("Expected ActivationMethodOneTimeToken to be 'oneTimeToken', got '%s'", ActivationMethodOneTimeToken)
1717
}
18-
if ActivationMethodLicenseKey != "licenseKey" {
19-
t.Errorf("Expected ActivationMethodLicenseKey to be 'licenseKey', got '%s'", ActivationMethodLicenseKey)
20-
}
2118
}
2219

2320
func TestActivationMode_Constants(t *testing.T) {
@@ -73,7 +70,7 @@ func TestErrorResponse_JSONSerialization(t *testing.T) {
7370

7471
t.Run("Unmarshal ErrorResponse", func(t *testing.T) {
7572
jsonData := `{"errors":[{"message":"Test error"}]}`
76-
73+
7774
var errResp ErrorResponse
7875
err := json.Unmarshal([]byte(jsonData), &errResp)
7976
if err != nil {
@@ -121,9 +118,9 @@ func TestRegisterDeviceInput_JSONSerialization(t *testing.T) {
121118
func TestActivateDeviceInput_JSONSerialization(t *testing.T) {
122119
input := ActivateDeviceInput{
123120
Modules: []ProductModule{ProductModulePrivacy},
124-
ActivationMethod: ActivationMethodLicenseKey,
121+
ActivationMethod: ActivationMethodOneTimeToken,
125122
ActivationMode: ActivationModePassive,
126-
LicenseKey: "TEST-LICENSE-KEY",
123+
OneTimeToken: "TEST-TOKEN",
127124
}
128125

129126
data, err := json.Marshal(input)
@@ -140,9 +137,6 @@ func TestActivateDeviceInput_JSONSerialization(t *testing.T) {
140137
if unmarshaled.ActivationMethod != input.ActivationMethod {
141138
t.Errorf("Expected ActivationMethod '%s', got '%s'", input.ActivationMethod, unmarshaled.ActivationMethod)
142139
}
143-
if unmarshaled.LicenseKey != input.LicenseKey {
144-
t.Errorf("Expected LicenseKey '%s', got '%s'", input.LicenseKey, unmarshaled.LicenseKey)
145-
}
146140
}
147141

148142
func TestHolocronResponse_JSONSerialization(t *testing.T) {
@@ -190,11 +184,11 @@ func TestDeviceOutput_JSONSerialization(t *testing.T) {
190184
}
191185

192186
if unmarshaled.Device.InstallationToken != output.Device.InstallationToken {
193-
t.Errorf("Expected InstallationToken '%s', got '%s'",
187+
t.Errorf("Expected InstallationToken '%s', got '%s'",
194188
output.Device.InstallationToken, unmarshaled.Device.InstallationToken)
195189
}
196190
if unmarshaled.DeviceModules.Privacy.Status != output.DeviceModules.Privacy.Status {
197-
t.Errorf("Expected Status '%s', got '%s'",
191+
t.Errorf("Expected Status '%s', got '%s'",
198192
output.DeviceModules.Privacy.Status, unmarshaled.DeviceModules.Privacy.Status)
199193
}
200194
}
@@ -224,7 +218,7 @@ func TestVpnIpAddresses_JSONSerialization(t *testing.T) {
224218
t.Errorf("Expected IPv6 '%s', got '%s'", ipAddrs.IpV6, unmarshaled.IpV6)
225219
}
226220
if unmarshaled.KeyExpirationHours != ipAddrs.KeyExpirationHours {
227-
t.Errorf("Expected KeyExpirationHours %d, got %d",
221+
t.Errorf("Expected KeyExpirationHours %d, got %d",
228222
ipAddrs.KeyExpirationHours, unmarshaled.KeyExpirationHours)
229223
}
230224
}
@@ -298,7 +292,7 @@ func TestServer_JSONSerialization(t *testing.T) {
298292
}
299293
if len(unmarshaled.PortRanges) > 0 {
300294
if unmarshaled.PortRanges[0].From != server.PortRanges[0].From {
301-
t.Errorf("Expected port range From %d, got %d",
295+
t.Errorf("Expected port range From %d, got %d",
302296
server.PortRanges[0].From, unmarshaled.PortRanges[0].From)
303297
}
304298
}
@@ -345,11 +339,11 @@ func TestVpnLocations_JSONSerialization(t *testing.T) {
345339
if len(unmarshaled.Countries) != len(locations.Countries) {
346340
t.Errorf("Expected %d countries, got %d", len(locations.Countries), len(unmarshaled.Countries))
347341
}
348-
342+
349343
if len(unmarshaled.Countries) > 0 {
350344
country := unmarshaled.Countries[0]
351345
originalCountry := locations.Countries[0]
352-
346+
353347
if country.Code != originalCountry.Code {
354348
t.Errorf("Expected country code '%s', got '%s'", originalCountry.Code, country.Code)
355349
}
@@ -389,7 +383,7 @@ func TestVpnClientDefaults_JSONSerialization(t *testing.T) {
389383
t.Errorf("Expected IPv4 MTU %d, got %d", defaults.IPv4.MTU, unmarshaled.IPv4.MTU)
390384
}
391385
if unmarshaled.KeyExpirationHours != defaults.KeyExpirationHours {
392-
t.Errorf("Expected KeyExpirationHours %d, got %d",
386+
t.Errorf("Expected KeyExpirationHours %d, got %d",
393387
defaults.KeyExpirationHours, unmarshaled.KeyExpirationHours)
394388
}
395389
}
@@ -412,7 +406,7 @@ func TestEmptyStructs_JSONSerialization(t *testing.T) {
412406
if err != nil {
413407
t.Errorf("Failed to marshal empty struct %T: %v", test, err)
414408
}
415-
409+
416410
// Basic check that we get valid JSON
417411
if len(data) == 0 {
418412
t.Errorf("Expected non-empty JSON for %T", test)
@@ -443,4 +437,4 @@ func TestNilSlicesAndPointers_JSONSerialization(t *testing.T) {
443437
if unmarshaled.Modules == nil {
444438
// This is acceptable behavior
445439
}
446-
}
440+
}

pkg/session/session.go

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ import (
1313
)
1414

1515
type SessionManager interface {
16-
LoginWithKey(string) error
17-
LoginWithCode(string) error
16+
LoginWithCode(code string) error
1817
Logout() error
1918
Active() bool
2019
}
@@ -31,16 +30,6 @@ func NewDefaultSessionManager(cfgProvider config.ConfigProvider, holocron remote
3130
}
3231
}
3332

34-
func (sm *DefaultSessionManager) LoginWithKey(key string) error {
35-
formattedKey := strings.ToUpper(key)
36-
37-
if len(formattedKey) != 23 {
38-
return errors.NewUserError("Invalid license key. License key should be 23 characters long.", errors.ErrInvalidInput)
39-
}
40-
41-
return sm.login(formattedKey, false)
42-
}
43-
4433
func (sm *DefaultSessionManager) LoginWithCode(code string) error {
4534
formattedCode := strings.ToUpper(code)
4635
formattedCode = strings.TrimPrefix(formattedCode, "MB-")
@@ -49,10 +38,10 @@ func (sm *DefaultSessionManager) LoginWithCode(code string) error {
4938
return errors.NewUserError("Invalid MB-code. MB-code should look like MB-XXXXXX or XXXXXX.", errors.ErrInvalidInput)
5039
}
5140

52-
return sm.login(formattedCode, true)
41+
return sm.login(formattedCode)
5342
}
5443

55-
func (sm *DefaultSessionManager) login(token string, mbcode bool) error {
44+
func (sm *DefaultSessionManager) login(code string) error {
5645
// Check current session
5746
if sm.Active() {
5847
return errors.NewUserError("There is an active session on your device. Try logout command first if you want to re-login.", nil)
@@ -74,7 +63,7 @@ func (sm *DefaultSessionManager) login(token string, mbcode bool) error {
7463
}
7564

7665
// Activate device
77-
m, err := sm.holocron.ActivateDevice(installationToken, token, mbcode)
66+
m, err := sm.holocron.ActivateDevice(installationToken, code)
7867
if err != nil {
7968
sm.cfgProvider.DeleteConfig()
8069
log.Errorf("Error activating the device: %v", err)

0 commit comments

Comments
 (0)