Skip to content

Commit

Permalink
Release v2.0.3 (#72)
Browse files Browse the repository at this point in the history
  • Loading branch information
shurwit committed Oct 21, 2022
2 parents 24e01c6 + aee52a0 commit b42ad68
Show file tree
Hide file tree
Showing 14 changed files with 602 additions and 77 deletions.
4 changes: 2 additions & 2 deletions .secrets.baseline
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@
"filename": "internal/testutils/test_utils.go",
"hashed_secret": "be4fc4886bd949b369d5e092eb87494f12e57e5b",
"is_verified": false,
"line_number": 59,
"line_number": 61,
"is_secret": false
}
],
Expand All @@ -139,5 +139,5 @@
}
]
},
"generated_at": "2022-08-16T16:45:18Z"
"generated_at": "2022-10-19T17:50:11Z"
}
9 changes: 7 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased
## [2.0.3] - 2022-10-21
### Added
- Scope utility functions [#70](https://github.com/rokwire/core-auth-library-go/issues/70)

## [2.0.2] - 2022-08-18
### Added
- Automate tests [#4](https://github.com/rokwire/core-auth-library-go/issues/4)
Expand Down Expand Up @@ -76,7 +79,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Initial release

[Unreleased]: https://github.com/rokwire/core-auth-library-go/compare/v2.0.1....HEAD
[Unreleased]: https://github.com/rokwire/core-auth-library-go/compare/v2.0.3....HEAD
[2.0.3]: https://github.com/rokwire/core-auth-library-go/compare/v2.0.2...v2.0.3
[2.0.2]: https://github.com/rokwire/core-auth-library-go/compare/v2.0.1...v2.0.2
[2.0.1]: https://github.com/rokwire/core-auth-library-go/compare/v1.0.9...v2.0.1
[1.0.9]: https://github.com/rokwire/core-auth-library-go/compare/v1.0.8...v1.0.9
[1.0.8]: https://github.com/rokwire/core-auth-library-go/compare/v1.0.7...v1.0.8
Expand Down
13 changes: 13 additions & 0 deletions CONVENTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,19 @@ Whenever a new interface is created, a unit test should be created for each func

When updating or changing existing implementations, run the associated unit tests to ensure that they still pass. If they do not, the implementation changes likely changed the interface as well. If the change to the interface was intentional, update the unit tests as needed to make them pass and document the [Breaking Change](#breaking-changes). If the change was not intentional, rework your implementation changes to keep the interface consistent and ensure all tests pass.

## Releases
Whenever a new release is made, the following process should be followed.

1. Make a pull request from `develop` into `main` named `Release vX.X.X` (eg. `Release v1.1.7`)
2. Review the changes included in the update to ensure they are all production ready.
3. Update the "Unreleased" version in the [CHANGELOG](CHANGELOG.md#unreleased) to `X.X.X - YYYY-MM-dd` (eg. `[1.1.7] - 2022-06-08`) on the `develop` branch.
4. Update [SECURITY.md](SECURITY.md) to reflect the latest supported and unsupported versions on the `develop` branch.
5. Update the latest version in any docs or source code as needed on the `develop` branch.
6. Make any changes needed to document [breaking changes](#breaking-changes) and [deprecations](#deprecations).
7. Merge the pull request using "Create a merge commit"
8. Create a new tag from the `main` branch called `vX.X.X` (eg. `v1.1.7`)
9. **RECOMMENDED** - Publish a new [GitHub Release](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository#creating-a-release) from this tag with the title `vX.X.X` (eg. `v1.1.7`). Include the contents from the [CHANGELOG](CHANGELOG.md) for this latest version in the release notes, as well as a link to the whole [CHANGELOG](CHANGELOG.md) on the `main` branch. For libraries this is highly recommended.

## Breaking Changes
Breaking changes should be avoided when possible, but will sometimes be necessary. In the event that a breaking change does need to be made, this change should be documented clearly for developers relying on the functionality. This includes the following items:
* Create and apply a "breaking" label to the associated issue in GitHub
Expand Down
4 changes: 2 additions & 2 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
Patches for **Core Auth Library** in this repository will only be applied to the following versions:
| Version | Supported |
| ------- | ------------------ |
| 2.0.2 | :white_check_mark: |
| < 2.0.2 | :x: |
| 2.0.3 | :white_check_mark: |
| < 2.0.3 | :x: |

## Reporting a Bug or Vulnerability

Expand Down
179 changes: 165 additions & 14 deletions authorization/authorization.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ const (
ScopeAll string = "all"
// ScopeGlobal represents the global scope
ScopeGlobal string = "all:all:all"
//ScopeOperationGet indicates a scope may be used for a get operation
ScopeOperationGet string = "get"
//ScopeOperationCreate indicates a scope may be used for a create operation
ScopeOperationCreate string = "create"
//ScopeOperationUpdate indicates a scope may be used for a update operation
ScopeOperationUpdate string = "update"
//ScopeOperationDelete indicates a scope may be used for a delete operation
ScopeOperationDelete string = "delete"
)

var (
Expand All @@ -48,6 +56,7 @@ type CasbinAuthorization struct {
}

// Any will validate that if the casbin enforcer gives access to one or more of the provided values
//
// Returns nil on success and error on failure.
func (c *CasbinAuthorization) Any(values []string, object string, action string) error {
for _, value := range values {
Expand All @@ -60,6 +69,7 @@ func (c *CasbinAuthorization) Any(values []string, object string, action string)
}

// All will validate that if the casbin enforcer gives access to all the provided values
//
// Returns nil on success and error on failure.
func (c *CasbinAuthorization) All(values []string, object string, action string) error {
for _, value := range values {
Expand Down Expand Up @@ -98,6 +108,7 @@ type CasbinScopeAuthorization struct {
}

// Any will validate that if the Casbin enforcer gives access to one or more of the provided values
//
// Returns nil on success and error on failure.
func (c *CasbinScopeAuthorization) Any(values []string, object string, action string) error {
if CheckScopesGlobals(values, c.serviceID) {
Expand All @@ -110,7 +121,7 @@ func (c *CasbinScopeAuthorization) Any(values []string, object string, action st
continue
}

if !matchScopeField(scope.ServiceID, c.serviceID) {
if !matchScopeField(scope.ServiceID, c.serviceID, false) {
continue
}

Expand All @@ -123,6 +134,7 @@ func (c *CasbinScopeAuthorization) Any(values []string, object string, action st
}

// All will validate that if the Casbin enforcer gives access to all the provided values
//
// Returns nil on success and error on failure.
func (c *CasbinScopeAuthorization) All(values []string, object string, action string) error {
for _, value := range values {
Expand All @@ -136,7 +148,7 @@ func (c *CasbinScopeAuthorization) All(values []string, object string, action st
continue
}

if !matchScopeField(scope.ServiceID, c.serviceID) {
if !matchScopeField(scope.ServiceID, c.serviceID, false) {
return reqErr
}

Expand Down Expand Up @@ -168,34 +180,72 @@ type Scope struct {
}

// String converts the scope to the string representation
func (s *Scope) String() string {
func (s Scope) String() string {
return fmt.Sprintf("%s:%s:%s", s.ServiceID, s.Resource, s.Operation)
}

// Match returns true if the scope matches the provided "other" scope
func (s *Scope) Match(other *Scope) bool {
if !matchScopeField(s.ServiceID, other.ServiceID) {
// Grants returns true if the scope (we have) grants access to the provided "want" scope
func (s Scope) Grants(want Scope) bool {
if !matchScopeField(s.ServiceID, want.ServiceID, false) {
return false
}

if !matchScopeField(s.Resource, want.Resource, true) {
return false
}

if !matchScopeField(s.Operation, want.Operation, false) {
return false
}

return true
}

// IsSub returns true if the scope is a sub-scope of the provided "super" scope
func (s Scope) IsSub(super Scope) bool {
if !matchScopeField(s.ServiceID, super.ServiceID, false) {
return false
}

if !matchScopeField(s.Resource, other.Resource) {
if !matchScopeField(super.Resource, s.Resource, true) {
return false
}

if !matchScopeField(s.Operation, other.Operation) {
if !matchScopeField(s.Operation, super.Operation, false) {
return false
}

return true
}

// AssociatedResources returns the subset of scope resources that s grants access to or that grant access to s,
// and a boolean indicator if a direct asymmetric match is found
//
// Optionally trims the Resource of s from matched scopes' Resources
func (s Scope) AssociatedResources(scopes []Scope, trimResource bool) (bool, []string) {
relevant := make([]string, 0)
for _, scope := range scopes {
if scope.Grants(s) {
return true, nil
}
if scope.IsSub(s) {
resource := scope.Resource
if trimResource {
resource = strings.TrimPrefix(resource, s.Resource+".")
}
relevant = append(relevant, resource)
}
}
return false, relevant
}

// IsGlobal returns true if the scope is the global scope
func (s *Scope) IsGlobal() bool {
func (s Scope) IsGlobal() bool {
return s.ServiceID == ScopeAll && s.Resource == ScopeAll && s.Operation == ScopeAll
}

// IsServiceGlobal returns true if the scope is the service-level global scope
func (s *Scope) IsServiceGlobal(serviceID string) bool {
func (s Scope) IsServiceGlobal(serviceID string) bool {
return s.ServiceID == serviceID && s.Resource == ScopeAll && s.Operation == ScopeAll
}

Expand All @@ -208,6 +258,102 @@ func ScopeFromString(scope string) (*Scope, error) {
return &Scope{ServiceID: comps[0], Resource: comps[1], Operation: comps[2]}, nil
}

// ScopesFromStrings creates a list of scope objects from a list of string representations.
// If skipInvalid is true, invalid scopes will be skipped, if false an error will be returned
func ScopesFromStrings(scopeStrings []string, skipInvalid bool) ([]Scope, error) {
var scopes []Scope
for _, s := range scopeStrings {
scope, err := ScopeFromString(s)
if err != nil {
if !skipInvalid {
return nil, err
}
} else {
scopes = append(scopes, *scope)
}
}
return scopes, nil
}

// ScopesToStrings creates a list of string representations from a list of scope objects
func ScopesToStrings(scopes []Scope) []string {
scopeStrings := make([]string, len(scopes))
for i, s := range scopes {
scopeStrings[i] = s.String()
}
return scopeStrings
}

// ListGrants returns true if any of the listed scopes grant the provided "want" scope
func ListGrants(scopes []Scope, want Scope) bool {
if len(scopes) == 0 {
return false
}

for _, s := range scopes {
if s.Grants(want) {
return true
}
}

return false
}

// ListGranted returns true if any or all scopes listed are granted by the provided "have" scope
func ListGranted(scopes []Scope, have Scope, all bool) bool {
if len(scopes) == 0 {
return false
}

for _, s := range scopes {
if have.Grants(s) {
if !all {
return true
}
} else if all {
return false
}
}
return all
}

// ResourceAccessForScopes checks which resources a list of scopes grant access to
//
// Inputs:
// scopes ([]Scope): list of scopes that have been granted
// minAllAccessScope (Scope): minimum access scope that grants access to all requested resources
// requestedResources: ([]string): list of data keys for which scope access should be checked
// Outputs:
// allAccess (bool): whether any scope in scopes grants access to all requested resources
// accessKeys ([]string): all data keys the provided scopes grant access to in minAllAccessScope context
// err (error): returned if scopes do not grant access to all requested resources
func ResourceAccessForScopes(scopes []Scope, minAllAccessScope Scope, requestedResources []string) (bool, []string, error) {
// get scopes relevant to determining resource access
allAccess, associatedResources := minAllAccessScope.AssociatedResources(scopes, true)
if allAccess {
return true, nil, nil
}
if len(associatedResources) == 0 {
return false, nil, fmt.Errorf("no associated scopes for resource %s", minAllAccessScope.Resource)
}

// check all requested resources against relevant scopes
for _, resource := range requestedResources {
validRequest := false
for _, associatedResource := range associatedResources {
if matchScopeField(associatedResource, resource, true) {
validRequest = true
break
}
}
if !validRequest {
return false, nil, fmt.Errorf("provided scopes do not grant %s access to resource %s", minAllAccessScope.Operation, resource)
}
}

return false, associatedResources, nil
}

// ScopeServiceGlobal returns the global scope
func ScopeServiceGlobal(serviceID string) string {
return fmt.Sprintf("%s:%s:%s", serviceID, ScopeAll, ScopeAll)
Expand All @@ -229,9 +375,14 @@ func CheckScopesGlobals(scopes []string, serviceID string) bool {
return authutils.ContainsString(scopes, serviceAll)
}

func matchScopeField(x string, y string) bool {
if x != y && x != ScopeAll && y != ScopeAll {
return false
// Returns whether the provided scope fields match each other
// Inputs:
// have (string): scope field to compare against
// want (string): scope field to be compared
// matchPrefix (bool): If true, uses prefixes to match, if false uses equality
func matchScopeField(have string, want string, matchPrefix bool) bool {
if have == want || have == ScopeAll || (matchPrefix && strings.HasPrefix(want, have)) {
return true
}
return true
return false
}

0 comments on commit b42ad68

Please sign in to comment.