Skip to content

Commit 09a82cb

Browse files
committed
feat(api,cli): device store
1 parent 448bdcb commit 09a82cb

22 files changed

+702
-683
lines changed

api/routes/device.go

+39-40
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"strconv"
66

77
"github.com/shellhub-io/shellhub/api/pkg/gateway"
8-
"github.com/shellhub-io/shellhub/pkg/api/query"
98
"github.com/shellhub-io/shellhub/pkg/api/requests"
109
"github.com/shellhub-io/shellhub/pkg/models"
1110
)
@@ -44,45 +43,45 @@ func (h *Handler) GetDeviceList(c gateway.Context) error {
4443
return err
4544
}
4645

47-
if c.QueryParam("connector") != "" {
48-
filter := []query.Filter{
49-
{
50-
Type: query.FilterTypeProperty,
51-
Params: &query.FilterProperty{
52-
Name: "info.platform",
53-
Operator: "eq",
54-
Value: "connector",
55-
},
56-
},
57-
{
58-
Type: query.FilterTypeOperator,
59-
Params: &query.FilterOperator{
60-
Name: "and",
61-
},
62-
},
63-
}
64-
65-
req.Filters.Data = append(req.Filters.Data, filter...)
66-
} else {
67-
filter := []query.Filter{
68-
{
69-
Type: query.FilterTypeProperty,
70-
Params: &query.FilterProperty{
71-
Name: "info.platform",
72-
Operator: "ne",
73-
Value: "connector",
74-
},
75-
},
76-
{
77-
Type: query.FilterTypeOperator,
78-
Params: &query.FilterOperator{
79-
Name: "and",
80-
},
81-
},
82-
}
83-
84-
req.Filters.Data = append(req.Filters.Data, filter...)
85-
}
46+
// if c.QueryParam("connector") != "" {
47+
// filter := []query.Filter{
48+
// {
49+
// Type: query.FilterTypeProperty,
50+
// Params: &query.FilterProperty{
51+
// Name: "info.platform",
52+
// Operator: "eq",
53+
// Value: "connector",
54+
// },
55+
// },
56+
// {
57+
// Type: query.FilterTypeOperator,
58+
// Params: &query.FilterOperator{
59+
// Name: "and",
60+
// },
61+
// },
62+
// }
63+
//
64+
// req.Filters.Data = append(req.Filters.Data, filter...)
65+
// } else {
66+
// filter := []query.Filter{
67+
// {
68+
// Type: query.FilterTypeProperty,
69+
// Params: &query.FilterProperty{
70+
// Name: "info.platform",
71+
// Operator: "ne",
72+
// Value: "connector",
73+
// },
74+
// },
75+
// {
76+
// Type: query.FilterTypeOperator,
77+
// Params: &query.FilterOperator{
78+
// Name: "and",
79+
// },
80+
// },
81+
// }
82+
//
83+
// req.Filters.Data = append(req.Filters.Data, filter...)
84+
// }
8685

8786
if err := c.Validate(req); err != nil {
8887
return err

api/services/auth.go

+53-90
Original file line numberDiff line numberDiff line change
@@ -68,119 +68,82 @@ type AuthService interface {
6868
}
6969

7070
func (s *service) AuthDevice(ctx context.Context, req requests.DeviceAuth, remoteAddr string) (*models.DeviceAuthResponse, error) {
71-
var identity *models.DeviceIdentity
72-
if req.Identity != nil {
73-
identity = &models.DeviceIdentity{
74-
MAC: req.Identity.MAC,
75-
}
71+
if _, err := s.store.NamespaceGet(ctx, store.NamespaceIdentTenantID, req.TenantID); err != nil {
72+
return nil, NewErrNamespaceNotFound(req.TenantID, err)
7673
}
7774

78-
var hostname string
79-
if req.Hostname != "" {
80-
hostname = req.Hostname
81-
}
75+
if req.Hostname == "" {
76+
if req.Identity == nil || req.Identity.MAC == "" {
77+
return nil, NewErrAuthDeviceNoIdentityAndHostname()
78+
}
8279

83-
if hostname == "" && (identity == nil || identity.MAC == "") {
84-
return nil, NewErrAuthDeviceNoIdentityAndHostname()
80+
req.Hostname = strings.ReplaceAll(req.Identity.MAC, ":", "-")
8581
}
8682

8783
auth := models.DeviceAuth{
88-
Hostname: hostname,
89-
Identity: identity,
84+
Hostname: req.Hostname,
85+
Identity: &models.DeviceIdentity{MAC: req.Identity.MAC},
9086
PublicKey: req.PublicKey,
9187
TenantID: req.TenantID,
9288
}
9389

94-
uid := sha256.Sum256(structhash.Dump(auth, 1))
95-
key := hex.EncodeToString(uid[:])
96-
97-
claims := authorizer.DeviceClaims{
98-
UID: key,
99-
TenantID: req.TenantID,
100-
}
101-
102-
token, err := jwttoken.EncodeDeviceClaims(claims, s.privKey)
90+
uidSHA := sha256.Sum256(structhash.Dump(auth, 1))
91+
device, err := s.store.DeviceGet(ctx, store.DeviceIdentUID, hex.EncodeToString(uidSHA[:]))
10392
if err != nil {
104-
return nil, NewErrTokenSigned(err)
105-
}
106-
107-
type Device struct {
108-
Name string
109-
Namespace string
110-
}
111-
112-
var value *Device
113-
114-
if err := s.cache.Get(ctx, strings.Join([]string{"auth_device", key}, "/"), &value); err == nil && value != nil {
115-
return &models.DeviceAuthResponse{
116-
UID: key,
117-
Token: token,
118-
Name: value.Name,
119-
Namespace: value.Namespace,
120-
}, nil
121-
}
122-
var info *models.DeviceInfo
123-
if req.Info != nil {
124-
info = &models.DeviceInfo{
125-
ID: req.Info.ID,
126-
PrettyName: req.Info.PrettyName,
127-
Version: req.Info.Version,
128-
Arch: req.Info.Arch,
129-
Platform: req.Info.Platform,
93+
if err != store.ErrNoDocuments {
94+
return nil, err
13095
}
131-
}
132-
133-
position, err := s.locator.GetPosition(net.ParseIP(remoteAddr))
134-
if err != nil {
135-
return nil, err
136-
}
13796

138-
device := models.Device{
139-
UID: key,
140-
Identity: identity,
141-
Info: info,
142-
PublicKey: req.PublicKey,
143-
TenantID: req.TenantID,
144-
LastSeen: clock.Now(),
145-
RemoteAddr: remoteAddr,
146-
Position: &models.DevicePosition{
147-
Longitude: position.Longitude,
148-
Latitude: position.Latitude,
149-
},
150-
}
97+
position, err := s.locator.GetPosition(net.ParseIP(remoteAddr))
98+
if err != nil {
99+
return nil, err
100+
}
151101

152-
// The order here is critical as we don't want to register devices if the tenant id is invalid
153-
namespace, err := s.store.NamespaceGet(ctx, store.NamespaceIdentTenantID, device.TenantID)
154-
if err != nil {
155-
return nil, NewErrNamespaceNotFound(device.TenantID, err)
156-
}
102+
device = &models.Device{
103+
UID: hex.EncodeToString(uidSHA[:]),
104+
TenantID: req.TenantID,
105+
LastSeen: clock.Now(),
106+
DisconnectedAt: &time.Time{},
107+
Status: models.DeviceStatusPending,
108+
Name: req.Hostname,
109+
Identity: &models.DeviceIdentity{MAC: req.Identity.MAC},
110+
PublicKey: req.PublicKey,
111+
Position: &models.DevicePosition{
112+
Longitude: position.Longitude,
113+
Latitude: position.Latitude,
114+
},
115+
Info: nil,
116+
}
157117

158-
hostname = strings.ToLower(hostname)
118+
if req.Info != nil {
119+
device.Info = &models.DeviceInfo{
120+
ID: req.Info.ID,
121+
PrettyName: req.Info.PrettyName,
122+
Version: req.Info.Version,
123+
Arch: req.Info.Arch,
124+
Platform: req.Info.Platform,
125+
}
126+
}
159127

160-
if err := s.store.DeviceCreate(ctx, device, hostname); err != nil {
161-
return nil, NewErrDeviceCreate(device, err)
162-
}
128+
if _, err := s.store.DeviceCreate(ctx, device); err != nil {
129+
return nil, NewErrDeviceCreate(*device, err)
130+
}
131+
} else {
132+
device.DisconnectedAt = nil
133+
device.LastSeen = clock.Now()
163134

164-
for _, uid := range req.Sessions {
165-
if err := s.store.SessionSetLastSeen(ctx, models.UID(uid)); err != nil {
166-
continue
135+
if err := s.store.DeviceSave(ctx, device); err != nil {
136+
log.WithError(err).Error("failed to updated device to online")
167137
}
168138
}
169139

170-
dev, err := s.store.DeviceGetByUID(ctx, models.UID(device.UID), device.TenantID)
140+
claims := authorizer.DeviceClaims{UID: device.UID, TenantID: device.TenantID}
141+
token, err := jwttoken.EncodeDeviceClaims(claims, s.privKey)
171142
if err != nil {
172-
return nil, NewErrDeviceNotFound(models.UID(device.UID), err)
173-
}
174-
if err := s.cache.Set(ctx, strings.Join([]string{"auth_device", key}, "/"), &Device{Name: dev.Name, Namespace: namespace.Name}, time.Second*30); err != nil {
175-
return nil, err
143+
return nil, NewErrTokenSigned(err)
176144
}
177145

178-
return &models.DeviceAuthResponse{
179-
UID: key,
180-
Token: token,
181-
Name: dev.Name,
182-
Namespace: namespace.Name,
183-
}, nil
146+
return &models.DeviceAuthResponse{UID: device.UID, Token: token, Name: device.Name, Namespace: "dev"}, nil
184147
}
185148

186149
func (s *service) AuthLocalUser(ctx context.Context, req *requests.AuthLocalUser, sourceIP string) (*models.UserAuthResponse, int64, string, error) {

0 commit comments

Comments
 (0)