1 package database
2
3 import (
4 "context"
5 "encoding/json"
6 "fmt"
7 "strings"
8 "time"
9
10 "github.com/ory/fosite"
11 "github.com/pkg/errors"
12
13 "edge-infra.dev/pkg/edge/iam/client"
14 )
15
16
17 type ClientProfileDB struct {
18 ClientName string `json:"clientName"`
19 ID string `json:"id"`
20 Name string `json:"name"`
21 Owner string `json:"owner"`
22 RedirectURIs []string `json:"redirect_uris"`
23 PrintBarcodeURI string `json:"print_barcode_uri"`
24 PrintBarcodeTypes []string `json:"print_barcode_types"`
25 GrantTypes []string `json:"grant_types"`
26 ResponseTypes []string `json:"response_types"`
27 ResponseModes []string `json:"response_modes"`
28 Scopes []string `json:"scopes"`
29 Audience []string `json:"audience"`
30 Roles []string `json:"roles"`
31 Public bool `json:"public"`
32 }
33
34
35 type ClientCredentialsDB struct {
36 Secret string `json:"client_secret"`
37 }
38
39
40
41 func (s *Store) GetClient(ctx context.Context, id string) (fosite.Client, error) {
42 return s.GetIAMClient(ctx, id)
43 }
44
45
46
47 func (s *Store) ClientAssertionJWTValid(_ context.Context, _ string) error {
48 return nil
49 }
50
51
52
53
54
55 func (s *Store) SetClientAssertionJWT(_ context.Context, _ string, _ time.Time) error {
56 return nil
57 }
58
59
60 func (s *Store) GetIAMClient(ctx context.Context, id string) (*client.Client, error) {
61 profileKey := keyFrom(KeyPrefixClientProfile, id)
62 var doc *Doc
63 var err error
64 if doc, err = s.getDoc(ctx, profileKey); doc == nil || err != nil {
65 return nil, errors.New("failed to get client in storage")
66 }
67 profile := doc.Value
68
69 credsKey := keyFrom(KeyPrefixClientCreds, id)
70 if doc, err = s.getDoc(ctx, credsKey); err != nil {
71 return nil, err
72 }
73 if doc == nil {
74 return nil, errors.New("no credentials found for client in storage`")
75 }
76 creds := doc.Value
77
78 iamClient, err := toEdgeIAMClient(id, []byte(profile), []byte(creds))
79 if err != nil {
80 return nil, err
81 }
82
83 return iamClient, nil
84 }
85
86 func (s *Store) GetClients(ctx context.Context, owner string) ([]*client.Client, error) {
87 query := map[string]interface{}{
88 "selector": map[string]interface{}{
89 "value": map[string]interface{}{
90 "owner": owner,
91 },
92 },
93 }
94 docs, err := s.findDoc(ctx, query)
95 if err != nil {
96 s.Log.Info("failed to find clients from db", "owner", owner)
97 return nil, err
98 }
99
100 ids := make([]string, 0)
101 for _, doc := range docs {
102 currentKey := doc.ID
103 res := strings.Split(currentKey, "client-profile:")
104 ids = append(ids, res[1])
105 }
106
107
108 iamClients := make([]*client.Client, 0)
109 for _, id := range ids {
110 c, err := s.GetIAMClient(ctx, id)
111 if err != nil {
112 s.Log.Info("failed to retrieve client from db", "id", id)
113 continue
114 }
115
116 if c != nil {
117 iamClients = append(iamClients, c)
118 }
119 }
120
121 return iamClients, nil
122 }
123
124 func (s *Store) SaveClientProfile(ctx context.Context, clientID string, profile *client.Profile) (*client.Profile, error) {
125 p := toClientProfileDB(profile)
126 p.ID = clientID
127
128 profileKey := keyFrom(KeyPrefixClientProfile, clientID)
129 profileJSON, err := json.Marshal(p)
130 if err != nil {
131 return nil, errors.WithStack(err)
132 }
133
134 if err := s.updateDoc(ctx, profileKey, profileJSON); err != nil {
135 return nil, errors.Wrap(err, "failed to save client profile")
136 }
137
138 return profile, nil
139 }
140
141 func (s *Store) SaveClientCredentials(ctx context.Context, clientID string, credentials *client.Credentials) (*client.Credentials, error) {
142 creds := toClientCredentialsDB(credentials)
143 credsKey := keyFrom(KeyPrefixClientCreds, clientID)
144
145 credsJSON, err := json.Marshal(creds)
146 if err != nil {
147 return nil, errors.WithStack(err)
148 }
149 if err := s.updateDoc(ctx, credsKey, credsJSON); err != nil {
150 return nil, errors.Wrap(err, "failed to save client credentials")
151 }
152
153 return credentials, nil
154 }
155
156 func (s *Store) DeleteClient(ctx context.Context, id string) error {
157 credsKey := keyFrom(KeyPrefixClientCreds, id)
158 if err := s.deleteDoc(ctx, credsKey); err != nil {
159 return errors.Wrap(err, fmt.Sprintf("failed to delete key '%v'", credsKey))
160 }
161
162 profileKey := keyFrom(KeyPrefixClientProfile, id)
163 if err := s.deleteDoc(ctx, profileKey); err != nil {
164 return errors.Wrap(err, fmt.Sprintf("failed to delete key '%v'", profileKey))
165 }
166
167 return nil
168 }
169
170 func toClientProfile(profileDB []byte) (*client.Profile, error) {
171 var profile ClientProfileDB
172 if err := json.Unmarshal(profileDB, &profile); err != nil {
173 return nil, errors.WithStack(err)
174 }
175
176 return &client.Profile{
177 Name: profile.Name,
178 ClientName: profile.ClientName,
179 Owner: profile.Owner,
180 RedirectURIs: profile.RedirectURIs,
181 ResponseModes: profile.ResponseModes,
182 PrintBarcodeURI: profile.PrintBarcodeURI,
183 PrintBarcodeTypes: profile.PrintBarcodeTypes,
184 Scopes: profile.Scopes,
185 GrantTypes: profile.GrantTypes,
186 ResponseTypes: profile.ResponseTypes,
187 Roles: profile.Roles,
188 Public: profile.Public,
189 }, nil
190 }
191
192 func toClientProfileDB(profile *client.Profile) *ClientProfileDB {
193 return &ClientProfileDB{
194 ClientName: profile.ClientName,
195 Name: profile.Name,
196 Owner: profile.Owner,
197 RedirectURIs: profile.RedirectURIs,
198 GrantTypes: profile.GrantTypes,
199 PrintBarcodeURI: profile.PrintBarcodeURI,
200 ResponseTypes: profile.ResponseTypes,
201 PrintBarcodeTypes: profile.PrintBarcodeTypes,
202 ResponseModes: profile.ResponseModes,
203 Scopes: profile.Scopes,
204 Audience: profile.Audience,
205 Roles: profile.Roles,
206 Public: profile.Public,
207 }
208 }
209
210 func toClientCredentials(credentialsDB []byte) (*client.Credentials, error) {
211 var creds ClientCredentialsDB
212 if err := json.Unmarshal(credentialsDB, &creds); err != nil {
213 return nil, errors.WithStack(err)
214 }
215
216 return &client.Credentials{
217 Secret: creds.Secret,
218 }, nil
219 }
220
221 func toClientCredentialsDB(creds *client.Credentials) *ClientCredentialsDB {
222 return &ClientCredentialsDB{
223 Secret: creds.Secret,
224 }
225 }
226
227 func toEdgeIAMClient(id string, profileDB []byte, credentialsDB []byte) (*client.Client, error) {
228 profile, err := toClientProfile(profileDB)
229 if err != nil {
230 return nil, errors.WithStack(err)
231 }
232
233 credentials, err := toClientCredentials(credentialsDB)
234 if err != nil {
235 return nil, errors.WithStack(err)
236 }
237
238 return &client.Client{
239 ID: id,
240 Profile: profile,
241 Credentials: credentials,
242 }, nil
243 }
244
View as plain text