Skip to content

Commit 9de391e

Browse files
authored
Merge pull request #194 from planetlabs/auth
Add support for the authentication extension
2 parents d7a3159 + c8d84a1 commit 9de391e

File tree

10 files changed

+1150
-48
lines changed

10 files changed

+1150
-48
lines changed

catalog.go

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ func GetCatalogExtension(uri string) Extension {
3434
}
3535

3636
func (catalog Catalog) MarshalJSON() ([]byte, error) {
37-
collectionMap := map[string]any{
37+
catalogMap := map[string]any{
3838
"type": "Catalog",
3939
}
4040
decoder, decoderErr := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
4141
TagName: "json",
42-
Result: &collectionMap,
42+
Result: &catalogMap,
4343
})
4444
if decoderErr != nil {
4545
return nil, decoderErr
@@ -54,10 +54,10 @@ func (catalog Catalog) MarshalJSON() ([]byte, error) {
5454
lookup := map[string]bool{}
5555

5656
for _, extension := range catalog.Extensions {
57-
if err := extension.Encode(collectionMap); err != nil {
57+
if err := extension.Encode(catalogMap); err != nil {
5858
return nil, err
5959
}
60-
uris, err := GetExtensionUris(collectionMap)
60+
uris, err := GetExtensionUris(catalogMap)
6161
if err != nil {
6262
return nil, err
6363
}
@@ -70,13 +70,26 @@ func (catalog Catalog) MarshalJSON() ([]byte, error) {
7070
}
7171
}
7272

73-
SetExtensionUris(collectionMap, extensionUris)
74-
return json.Marshal(collectionMap)
73+
links, linkExtensionUris, err := EncodeLinks(catalog.Links)
74+
if err != nil {
75+
return nil, err
76+
}
77+
catalogMap["links"] = links
78+
79+
for _, uri := range linkExtensionUris {
80+
if !lookup[uri] {
81+
extensionUris = append(extensionUris, uri)
82+
lookup[uri] = true
83+
}
84+
}
85+
86+
SetExtensionUris(catalogMap, extensionUris)
87+
return json.Marshal(catalogMap)
7588
}
7689

7790
func (catalog *Catalog) UnmarshalJSON(data []byte) error {
78-
collectionMap := map[string]any{}
79-
if err := json.Unmarshal(data, &collectionMap); err != nil {
91+
catalogMap := map[string]any{}
92+
if err := json.Unmarshal(data, &catalogMap); err != nil {
8093
return err
8194
}
8295

@@ -88,11 +101,11 @@ func (catalog *Catalog) UnmarshalJSON(data []byte) error {
88101
return decoderErr
89102
}
90103

91-
if err := decoder.Decode(collectionMap); err != nil {
104+
if err := decoder.Decode(catalogMap); err != nil {
92105
return err
93106
}
94107

95-
extensionUris, err := GetExtensionUris(collectionMap)
108+
extensionUris, err := GetExtensionUris(catalogMap)
96109
if err != nil {
97110
return err
98111
}
@@ -102,11 +115,15 @@ func (catalog *Catalog) UnmarshalJSON(data []byte) error {
102115
if extension == nil {
103116
continue
104117
}
105-
if err := extension.Decode(collectionMap); err != nil {
118+
if err := extension.Decode(catalogMap); err != nil {
106119
return fmt.Errorf("decoding error for %s: %w", uri, err)
107120
}
108121
catalog.Extensions = append(catalog.Extensions, extension)
109122
}
110123

124+
if err := decodeExtendedLinks(catalogMap, catalog.Links, extensionUris); err != nil {
125+
return err
126+
}
127+
111128
return nil
112129
}

collection.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,19 @@ func (collection Collection) MarshalJSON() ([]byte, error) {
9595
}
9696
}
9797

98+
links, linkExtensionUris, err := EncodeLinks(collection.Links)
99+
if err != nil {
100+
return nil, err
101+
}
102+
collectionMap["links"] = links
103+
104+
for _, uri := range linkExtensionUris {
105+
if !lookup[uri] {
106+
extensionUris = append(extensionUris, uri)
107+
lookup[uri] = true
108+
}
109+
}
110+
98111
SetExtensionUris(collectionMap, extensionUris)
99112
return json.Marshal(collectionMap)
100113
}
@@ -132,6 +145,10 @@ func (collection *Collection) UnmarshalJSON(data []byte) error {
132145
collection.Extensions = append(collection.Extensions, extension)
133146
}
134147

148+
if err := decodeExtendedLinks(collectionMap, collection.Links, extensionUris); err != nil {
149+
return err
150+
}
151+
135152
return nil
136153
}
137154

extension.go

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package stac
22

33
import (
4+
"errors"
45
"fmt"
56
"regexp"
67
"slices"
@@ -141,24 +142,93 @@ func DecodeExtendedItemProperties(itemExtension Extension, itemMap map[string]an
141142
return nil
142143
}
143144

144-
func EncodeExtendedAsset(assetExtension Extension, assetMap map[string]any) error {
145+
func EncodeExtendedMap(extension Extension, data map[string]any) error {
145146
encoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
146147
TagName: "json",
147-
Result: &assetMap,
148+
Result: &data,
148149
})
149150
if err != nil {
150151
return err
151152
}
152-
return encoder.Decode(assetExtension)
153+
return encoder.Decode(extension)
153154
}
154155

155-
func DecodeExtendedAsset(assetExtension Extension, assetMap map[string]any) error {
156+
func DecodeExtendedMap(extension Extension, data map[string]any) error {
156157
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
157158
TagName: "json",
158-
Result: assetExtension,
159+
Result: extension,
159160
})
160161
if err != nil {
161162
return err
162163
}
163-
return decoder.Decode(assetMap)
164+
return decoder.Decode(data)
164165
}
166+
167+
func decodeExtendedLinks(data map[string]any, links []*Link, extensionUris []string) error {
168+
linksValue, ok := data["links"]
169+
if !ok {
170+
return nil
171+
}
172+
linksData, ok := linksValue.([]any)
173+
if !ok {
174+
return fmt.Errorf("unexpected type for links: %t", linksValue)
175+
}
176+
177+
for i, link := range links {
178+
for _, uri := range extensionUris {
179+
extension := GetLinkExtension(uri)
180+
if extension == nil {
181+
continue
182+
}
183+
linkMap, ok := linksData[i].(map[string]any)
184+
if !ok {
185+
return fmt.Errorf("unexpected type for %q link: %t", i, linksData[i])
186+
}
187+
if err := extension.Decode(linkMap); err != nil {
188+
if errors.Is(err, ErrExtensionDoesNotApply) {
189+
continue
190+
}
191+
return fmt.Errorf("decoding error for %s: %w", uri, err)
192+
}
193+
link.Extensions = append(link.Extensions, extension)
194+
}
195+
}
196+
197+
return nil
198+
}
199+
200+
func decodeExtendedAssets(data map[string]any, assets map[string]*Asset, extensionUris []string) error {
201+
assetsValue, ok := data["assets"]
202+
if !ok {
203+
return nil
204+
}
205+
assetsMap, ok := assetsValue.(map[string]any)
206+
if !ok {
207+
return fmt.Errorf("unexpected type for assets: %t", assetsValue)
208+
}
209+
210+
for key, asset := range assets {
211+
for _, uri := range extensionUris {
212+
extension := GetAssetExtension(uri)
213+
if extension == nil {
214+
continue
215+
}
216+
assetMap, ok := assetsMap[key].(map[string]any)
217+
if !ok {
218+
return fmt.Errorf("unexpected type for %q asset: %t", key, assetsMap[key])
219+
}
220+
if err := extension.Decode(assetMap); err != nil {
221+
if errors.Is(err, ErrExtensionDoesNotApply) {
222+
continue
223+
}
224+
return fmt.Errorf("decoding error for %s: %w", uri, err)
225+
}
226+
asset.Extensions = append(asset.Extensions, extension)
227+
}
228+
}
229+
230+
return nil
231+
}
232+
233+
// ErrExtensionDoesNotApply is returned when decoding JSON and an extension referenced in stac_extensions does not apply to a value.
234+
var ErrExtensionDoesNotApply = errors.New("extension does not apply")

extensions/auth/auth.go

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
package auth
2+
3+
import (
4+
"fmt"
5+
"regexp"
6+
7+
"github.com/mitchellh/mapstructure"
8+
"github.com/planetlabs/go-stac"
9+
)
10+
11+
const (
12+
extensionUri = "https://stac-extensions.github.io/authentication/v1.1.0/schema.json"
13+
extensionPattern = `https://stac-extensions.github.io/authentication/v1\..*/schema.json`
14+
schemesKey = "auth:schemes"
15+
refsKey = "auth:refs"
16+
)
17+
18+
func init() {
19+
r := regexp.MustCompile(extensionPattern)
20+
21+
stac.RegisterCollectionExtension(r, func() stac.Extension { return &Collection{} })
22+
stac.RegisterCatalogExtension(r, func() stac.Extension { return &Catalog{} })
23+
stac.RegisterAssetExtension(r, func() stac.Extension { return &Asset{} })
24+
stac.RegisterLinkExtension(r, func() stac.Extension { return &Link{} })
25+
}
26+
27+
type Collection struct {
28+
Schemes map[string]*Scheme
29+
}
30+
31+
var _ stac.Extension = (*Collection)(nil)
32+
33+
func (*Collection) URI() string {
34+
return extensionUri
35+
}
36+
37+
func (e *Collection) Encode(collectionMap map[string]any) error {
38+
collectionMap[schemesKey] = e.Schemes
39+
return nil
40+
}
41+
42+
func (e *Collection) Decode(collectionMap map[string]any) error {
43+
schemes, err := decodeSchemes(collectionMap)
44+
if err != nil {
45+
return err
46+
}
47+
e.Schemes = schemes
48+
return nil
49+
}
50+
51+
type Catalog struct {
52+
Schemes map[string]*Scheme
53+
}
54+
55+
var _ stac.Extension = (*Catalog)(nil)
56+
57+
func (*Catalog) URI() string {
58+
return extensionUri
59+
}
60+
61+
func (e *Catalog) Encode(catalogMap map[string]any) error {
62+
catalogMap[schemesKey] = e.Schemes
63+
return nil
64+
}
65+
66+
func (e *Catalog) Decode(catalogMap map[string]any) error {
67+
schemes, err := decodeSchemes(catalogMap)
68+
if err != nil {
69+
return err
70+
}
71+
e.Schemes = schemes
72+
return nil
73+
}
74+
75+
func decodeSchemes(data map[string]any) (map[string]*Scheme, error) {
76+
schemesValue, ok := data[schemesKey]
77+
if !ok {
78+
return nil, nil
79+
}
80+
81+
schemesMap, ok := schemesValue.(map[string]any)
82+
if !ok {
83+
return nil, fmt.Errorf("expected %s to be an map[string]any, got %t", schemesKey, schemesValue)
84+
}
85+
86+
schemes := map[string]*Scheme{}
87+
for key, schemeValue := range schemesMap {
88+
schemeMap, ok := schemeValue.(map[string]any)
89+
if !ok {
90+
return nil, fmt.Errorf("expected scheme to be a map[string]any, got %t", schemeValue)
91+
}
92+
scheme := &Scheme{}
93+
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
94+
TagName: "json",
95+
Result: scheme,
96+
})
97+
if err != nil {
98+
return nil, err
99+
}
100+
if err := decoder.Decode(schemeMap); err != nil {
101+
return nil, err
102+
}
103+
104+
schemes[key] = scheme
105+
}
106+
return schemes, nil
107+
}
108+
109+
type Scheme struct {
110+
Type string `json:"type"`
111+
Description string `json:"description,omitempty"`
112+
Name string `json:"name,omitempty"`
113+
In string `json:"in,omitempty"`
114+
Scheme string `json:"scheme,omitempty"`
115+
Flows map[string]any `json:"flows,omitempty"`
116+
OpenIdConnectUrl string `json:"openIdConnectUrl,omitempty"`
117+
}
118+
119+
type Asset struct {
120+
Refs []string `json:"auth:refs,omitempty"`
121+
}
122+
123+
var _ stac.Extension = (*Asset)(nil)
124+
125+
func (*Asset) URI() string {
126+
return extensionUri
127+
}
128+
129+
func (e *Asset) Encode(assetMap map[string]any) error {
130+
return stac.EncodeExtendedMap(e, assetMap)
131+
}
132+
133+
func (e *Asset) Decode(assetMap map[string]any) error {
134+
if _, ok := assetMap[refsKey]; !ok {
135+
return stac.ErrExtensionDoesNotApply
136+
}
137+
return stac.DecodeExtendedMap(e, assetMap)
138+
}
139+
140+
type Link struct {
141+
Refs []string `json:"auth:refs,omitempty"`
142+
}
143+
144+
var _ stac.Extension = (*Link)(nil)
145+
146+
func (*Link) URI() string {
147+
return extensionUri
148+
}
149+
150+
func (e *Link) Encode(linkMap map[string]any) error {
151+
return stac.EncodeExtendedMap(e, linkMap)
152+
}
153+
154+
func (e *Link) Decode(linkMap map[string]any) error {
155+
if _, ok := linkMap[refsKey]; !ok {
156+
return stac.ErrExtensionDoesNotApply
157+
}
158+
return stac.DecodeExtendedMap(e, linkMap)
159+
}

0 commit comments

Comments
 (0)