Skip to content

Commit 486eeca

Browse files
committed
feat: add SSH settings for device and namespace
Add allow-based SSH settings for both namespace and device configuration, update the backend migrations and auth flow, and wire the React console to edit the new settings consistently. Closes #6136
1 parent 50df770 commit 486eeca

65 files changed

Lines changed: 2514 additions & 178 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

api/routes/device.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,10 @@ func (h *Handler) UpdateDevice(c gateway.Context) error {
251251
return err
252252
}
253253

254+
if c.Tenant() != nil {
255+
req.TenantID = c.Tenant().ID
256+
}
257+
254258
if err := h.service.UpdateDevice(c.Ctx(), req); err != nil {
255259
return err
256260
}

api/services/device.go

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -339,14 +339,51 @@ func (s *service) UpdateDevice(ctx context.Context, req *requests.DeviceUpdate)
339339
return NewErrDeviceNotFound(models.UID(req.UID), err)
340340
}
341341

342-
conflictsTarget := &models.DeviceConflicts{Name: req.Name}
343-
conflictsTarget.Distinct(device)
344-
if _, has, err := s.store.DeviceConflicts(ctx, conflictsTarget); err != nil || has {
345-
return NewErrDeviceDuplicated(req.Name, err)
342+
if req.Name != "" {
343+
conflictsTarget := &models.DeviceConflicts{Name: req.Name}
344+
conflictsTarget.Distinct(device)
345+
if _, has, err := s.store.DeviceConflicts(ctx, conflictsTarget); err != nil || has {
346+
return NewErrDeviceDuplicated(req.Name, err)
347+
}
348+
349+
if !strings.EqualFold(req.Name, device.Name) {
350+
device.Name = strings.ToLower(req.Name)
351+
}
346352
}
347353

348-
if req.Name != "" && !strings.EqualFold(req.Name, device.Name) {
349-
device.Name = strings.ToLower(req.Name)
354+
if req.SSH != nil {
355+
// Only initialize SSH if device doesn't have one and we're updating it
356+
if device.SSH == nil {
357+
device.SSH = models.DefaultSSHSettings()
358+
}
359+
360+
if req.SSH.AllowPassword != nil {
361+
device.SSH.AllowPassword = *req.SSH.AllowPassword
362+
}
363+
if req.SSH.AllowPublicKey != nil {
364+
device.SSH.AllowPublicKey = *req.SSH.AllowPublicKey
365+
}
366+
if req.SSH.AllowRoot != nil {
367+
device.SSH.AllowRoot = *req.SSH.AllowRoot
368+
}
369+
if req.SSH.AllowEmptyPasswords != nil {
370+
device.SSH.AllowEmptyPasswords = *req.SSH.AllowEmptyPasswords
371+
}
372+
if req.SSH.AllowTTY != nil {
373+
device.SSH.AllowTTY = *req.SSH.AllowTTY
374+
}
375+
if req.SSH.AllowTCPForwarding != nil {
376+
device.SSH.AllowTCPForwarding = *req.SSH.AllowTCPForwarding
377+
}
378+
if req.SSH.AllowWebEndpoints != nil {
379+
device.SSH.AllowWebEndpoints = *req.SSH.AllowWebEndpoints
380+
}
381+
if req.SSH.AllowSFTP != nil {
382+
device.SSH.AllowSFTP = *req.SSH.AllowSFTP
383+
}
384+
if req.SSH.AllowAgentForwarding != nil {
385+
device.SSH.AllowAgentForwarding = *req.SSH.AllowAgentForwarding
386+
}
350387
}
351388

352389
if err := s.store.DeviceUpdate(ctx, device); err != nil { // nolint:revive
@@ -376,6 +413,10 @@ func (s *service) mergeDevice(ctx context.Context, tenantID string, oldDevice *m
376413
}
377414

378415
log.WithFields(logFields).Debug("updating new device name to preserve old device identity")
416+
if oldDevice.SSH != nil {
417+
newDevice.SSH = oldDevice.SSH
418+
}
419+
379420
newDevice.Name = oldDevice.Name
380421
if err := s.store.DeviceUpdate(ctx, newDevice); err != nil {
381422
log.WithError(err).WithFields(logFields).Error("failed to update new device name")

api/services/device_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1699,13 +1699,35 @@ func TestUpdateDeviceStatus(t *testing.T) {
16991699
TenantID: "00000000-0000-0000-0000-000000000000",
17001700
Status: models.DeviceStatusAccepted,
17011701
Identity: &models.DeviceIdentity{MAC: "aa:bb:cc:dd:ee:ff"},
1702+
SSH: &models.SSHSettings{
1703+
AllowPassword: false,
1704+
AllowPublicKey: true,
1705+
AllowRoot: false,
1706+
AllowEmptyPasswords: true,
1707+
AllowTTY: false,
1708+
AllowTCPForwarding: true,
1709+
AllowWebEndpoints: false,
1710+
AllowSFTP: true,
1711+
AllowAgentForwarding: false,
1712+
},
17021713
}
17031714
mergedDevice := &models.Device{
17041715
UID: "new-device",
17051716
Name: "device-name",
17061717
TenantID: "00000000-0000-0000-0000-000000000000",
17071718
Status: models.DeviceStatusPending,
17081719
Identity: &models.DeviceIdentity{MAC: "aa:bb:cc:dd:ee:ff"},
1720+
SSH: &models.SSHSettings{
1721+
AllowPassword: false,
1722+
AllowPublicKey: true,
1723+
AllowRoot: false,
1724+
AllowEmptyPasswords: true,
1725+
AllowTTY: false,
1726+
AllowTCPForwarding: true,
1727+
AllowWebEndpoints: false,
1728+
AllowSFTP: true,
1729+
AllowAgentForwarding: false,
1730+
},
17091731
}
17101732
finalDevice := &models.Device{
17111733
UID: "new-device",
@@ -1714,6 +1736,17 @@ func TestUpdateDeviceStatus(t *testing.T) {
17141736
Status: models.DeviceStatusAccepted,
17151737
StatusUpdatedAt: now,
17161738
Identity: &models.DeviceIdentity{MAC: "aa:bb:cc:dd:ee:ff"},
1739+
SSH: &models.SSHSettings{
1740+
AllowPassword: false,
1741+
AllowPublicKey: true,
1742+
AllowRoot: false,
1743+
AllowEmptyPasswords: true,
1744+
AllowTTY: false,
1745+
AllowTCPForwarding: true,
1746+
AllowWebEndpoints: false,
1747+
AllowSFTP: true,
1748+
AllowAgentForwarding: false,
1749+
},
17171750
}
17181751

17191752
storeMock.

api/services/namespace.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,15 @@ func (s *service) CreateNamespace(ctx context.Context, req *requests.NamespaceCr
6666
Settings: &models.NamespaceSettings{
6767
SessionRecord: true,
6868
ConnectionAnnouncement: "",
69+
AllowPassword: true,
70+
AllowPublicKey: true,
71+
AllowRoot: true,
72+
AllowEmptyPasswords: true,
73+
AllowTTY: true,
74+
AllowTCPForwarding: true,
75+
AllowWebEndpoints: true,
76+
AllowSFTP: true,
77+
AllowAgentForwarding: true,
6978
},
7079
TenantID: req.TenantID,
7180
Type: models.NewDefaultType(),
@@ -176,6 +185,42 @@ func (s *service) EditNamespace(ctx context.Context, req *requests.NamespaceEdit
176185
namespace.Settings.ConnectionAnnouncement = *req.Settings.ConnectionAnnouncement
177186
}
178187

188+
if req.Settings.AllowPassword != nil {
189+
namespace.Settings.AllowPassword = *req.Settings.AllowPassword
190+
}
191+
192+
if req.Settings.AllowPublicKey != nil {
193+
namespace.Settings.AllowPublicKey = *req.Settings.AllowPublicKey
194+
}
195+
196+
if req.Settings.AllowRoot != nil {
197+
namespace.Settings.AllowRoot = *req.Settings.AllowRoot
198+
}
199+
200+
if req.Settings.AllowEmptyPasswords != nil {
201+
namespace.Settings.AllowEmptyPasswords = *req.Settings.AllowEmptyPasswords
202+
}
203+
204+
if req.Settings.AllowTTY != nil {
205+
namespace.Settings.AllowTTY = *req.Settings.AllowTTY
206+
}
207+
208+
if req.Settings.AllowTCPForwarding != nil {
209+
namespace.Settings.AllowTCPForwarding = *req.Settings.AllowTCPForwarding
210+
}
211+
212+
if req.Settings.AllowWebEndpoints != nil {
213+
namespace.Settings.AllowWebEndpoints = *req.Settings.AllowWebEndpoints
214+
}
215+
216+
if req.Settings.AllowSFTP != nil {
217+
namespace.Settings.AllowSFTP = *req.Settings.AllowSFTP
218+
}
219+
220+
if req.Settings.AllowAgentForwarding != nil {
221+
namespace.Settings.AllowAgentForwarding = *req.Settings.AllowAgentForwarding
222+
}
223+
179224
if err := s.store.NamespaceUpdate(ctx, namespace); err != nil {
180225
return nil, err
181226
}

0 commit comments

Comments
 (0)