-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #276 from xmidt-org/feature/capabilities
Feature/capabilities
- Loading branch information
Showing
8 changed files
with
209 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
// SPDX-FileCopyrightText: 2024 Comcast Cable Communications Management, LLC | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package bascule | ||
|
||
// CapabilitiesAccessor is an interface that any type may choose to implement | ||
// in order to provide access to any capabilities associated with the token. | ||
// Capabilities do not make sense for all tokens, e.g. simple basic auth tokens. | ||
type CapabilitiesAccessor interface { | ||
// Capabilities returns the set of capabilities associated with this token. | ||
Capabilities() []string | ||
} | ||
|
||
// GetCapabilities attempts to convert a value v into a slice of capabilities. | ||
// | ||
// This function provide very flexible values to be used as capabilities. This is | ||
// particularly useful when unmarshalling values, since those values may not be strings | ||
// or slices. | ||
// | ||
// The following conversions are attempted, in order: | ||
// | ||
// (1) If v implements CapabilitiesAccessor, then Capabilities() is returned. | ||
// | ||
// (2) If v is a []string, it is returned as is. | ||
// | ||
// (3) If v is a scalar string, a slice containing only that string is returned. | ||
// | ||
// (4) If v is a []any, a slice containing each element cast to a string is returned. | ||
// If any elements are not castable to string, this function considers that to be the | ||
// same as missing capabilities, i.e. false is returned with an empty slice. | ||
// | ||
// If any conversion was possible, this function returns true even if the capabilities were empty. | ||
// If no such conversion was possible, this function returns false. | ||
func GetCapabilities(v any) (caps []string, ok bool) { | ||
switch vt := v.(type) { | ||
case CapabilitiesAccessor: | ||
caps = vt.Capabilities() | ||
ok = true | ||
|
||
case []string: | ||
caps = vt | ||
ok = true | ||
|
||
case string: | ||
caps = []string{vt} | ||
ok = true | ||
|
||
case []any: | ||
converted := make([]string, 0, len(vt)) | ||
for _, raw := range vt { | ||
if element, isString := raw.(string); isString { | ||
converted = append(converted, element) | ||
} else { | ||
break | ||
} | ||
} | ||
|
||
if ok = len(converted) == len(vt); ok { | ||
caps = converted | ||
} | ||
} | ||
|
||
return | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
// SPDX-FileCopyrightText: 2024 Comcast Cable Communications Management, LLC | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package bascule | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/suite" | ||
) | ||
|
||
type CapabilitiesTestSuite struct { | ||
suite.Suite | ||
} | ||
|
||
func (suite *CapabilitiesTestSuite) testGetCapabilitiesNil() { | ||
caps, ok := GetCapabilities(nil) | ||
suite.False(ok) | ||
suite.Empty(caps) | ||
} | ||
|
||
func (suite *CapabilitiesTestSuite) testGetCapabilitiesAccessor() { | ||
suite.Run("NoCapabilities", func() { | ||
mt := new(mockToken) | ||
caps, ok := GetCapabilities(mt) | ||
suite.False(ok) | ||
suite.Empty(caps) | ||
|
||
mt.AssertExpectations(suite.T()) | ||
}) | ||
|
||
suite.Run("EmptyCapabilities", func() { | ||
mt := new(mockTokenWithCapabilities) | ||
mt.ExpectCapabilities().Once() | ||
caps, ok := GetCapabilities(mt) | ||
suite.True(ok) | ||
suite.Empty(caps) | ||
|
||
mt.AssertExpectations(suite.T()) | ||
}) | ||
|
||
suite.Run("HasCapabilities", func() { | ||
mt := new(mockTokenWithCapabilities) | ||
mt.ExpectCapabilities("one", "two", "three").Once() | ||
caps, ok := GetCapabilities(mt) | ||
suite.True(ok) | ||
suite.Equal([]string{"one", "two", "three"}, caps) | ||
|
||
mt.AssertExpectations(suite.T()) | ||
}) | ||
} | ||
|
||
func (suite *CapabilitiesTestSuite) testGetCapabilitiesStringSlice() { | ||
suite.Run("Empty", func() { | ||
caps, ok := GetCapabilities([]string{}) | ||
suite.True(ok) | ||
suite.Empty(caps) | ||
}) | ||
|
||
suite.Run("NonEmpty", func() { | ||
caps, ok := GetCapabilities([]string{"one", "two", "three"}) | ||
suite.True(ok) | ||
suite.Equal([]string{"one", "two", "three"}, caps) | ||
}) | ||
} | ||
|
||
func (suite *CapabilitiesTestSuite) testGetCapabilitiesString() { | ||
caps, ok := GetCapabilities("single") | ||
suite.True(ok) | ||
suite.Equal([]string{"single"}, caps) | ||
} | ||
|
||
func (suite *CapabilitiesTestSuite) testGetCapabilitiesAnySlice() { | ||
suite.Run("AllStrings", func() { | ||
caps, ok := GetCapabilities([]any{"one", "two", "three"}) | ||
suite.True(ok) | ||
suite.Equal([]string{"one", "two", "three"}, caps) | ||
}) | ||
|
||
suite.Run("NonStrings", func() { | ||
caps, ok := GetCapabilities([]any{"one", 2.0, 3}) | ||
suite.False(ok) | ||
suite.Empty(caps) | ||
}) | ||
} | ||
|
||
func (suite *CapabilitiesTestSuite) TestGetCapabilities() { | ||
suite.Run("Nil", suite.testGetCapabilitiesNil) | ||
suite.Run("Accessor", suite.testGetCapabilitiesAccessor) | ||
suite.Run("StringSlice", suite.testGetCapabilitiesStringSlice) | ||
suite.Run("String", suite.testGetCapabilitiesString) | ||
suite.Run("AnySlice", suite.testGetCapabilitiesAnySlice) | ||
} | ||
|
||
func TestCapabilities(t *testing.T) { | ||
suite.Run(t, new(CapabilitiesTestSuite)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters