Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unbeta permission #673

Merged
merged 16 commits into from
Jan 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@ Unreleased changes are available as `avenga/couper:edge` container.
* Use nested `jwt_signing_profile` block in [`oauth2` block](https://docs.couper.io/configuration/block/oauth2) for `grant_type` `"urn:ietf:params:oauth:grant-type:jwt-bearer"` in absence of `assertion` attribute ([#619](https://github.com/avenga/couper/pull/619))
* Improved the way an SPA `bootstrap_file` gets cached and served in combination with `bootstrap_data` ([#656](https://github.com/avenga/couper/pull/656))
* Harmonized and improved logged error information for references to undefined blocks ([#651](https://github.com/avenga/couper/pull/651))
* Unbeta permission features: ([#673](https://github.com/avenga/couper/pull/673))
* `beta_required_permission` attribute for [`api`](https://docs.couper.io/configuration/block/api#attribute-beta_required_permission) and [`endpoint`](https://docs.couper.io/configuration/block/endpoint#attribute-beta_required_permission) blocks,
* `beta_granted_permissions` and `beta_required_permission` [request context variables](https://docs.couper.io/configuration/variables#request),
* `beta_insufficient_permissions` [error type](https://docs.couper.io/configuration/error-handling/#api-error-types),
* `beta_permissions_claim`, `beta_permissions_map`, `beta_permissions_map_file`, `beta_roles_claim`, `beta_roles_map` and `beta_roles_map_file` attributes for [`jwt` block](https://docs.couper.io/configuration/block/jwt#attributes).

* **Fixed**
* Loop with evaluation error in [`custom_log_fields`](https://docs.couper.io/observation/logging#custom-logging) if log level is `"debug"` ([#659](https://github.com/avenga/couper/pull/659))
* Use of [backend-related variables](https://docs.couper.io/configuration/variables#backend) in [`custom_log_fields`](https://docs.couper.io/observation/logging#custom-logging) within a [`backend` block](https://docs.couper.io/configuration/block/backend) ([#658](https://github.com/avenga/couper/pull/658))
* Loop with evaluation error in [`custom_log_fields`](https://docs.couper.io/observation/logging#custom-logging) if log level is `"debug"` ([#659](https://github.com/avenga/couper/pull/659))

---

Expand Down
6 changes: 3 additions & 3 deletions accesscontrol/jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ func newJWT(options *JWTOptions) (*JWT, error) {
}

if options.RolesClaim != "" && options.RolesMap == nil {
return nil, fmt.Errorf("missing beta_roles_map")
return nil, fmt.Errorf("missing roles_map")
}

jwtAC := &JWT{
Expand Down Expand Up @@ -257,11 +257,11 @@ func (j *JWT) Validate(req *http.Request) error {
log := req.Context().Value(request.LogEntry).(*logrus.Entry).WithContext(req.Context())
jwtGrantedPermissions := j.getGrantedPermissions(tokenClaims, log)

grantedPermissions, _ := ctx.Value(request.BetaGrantedPermissions).([]string)
grantedPermissions, _ := ctx.Value(request.GrantedPermissions).([]string)

grantedPermissions = append(grantedPermissions, jwtGrantedPermissions...)

ctx = context.WithValue(ctx, request.BetaGrantedPermissions, grantedPermissions)
ctx = context.WithValue(ctx, request.GrantedPermissions, grantedPermissions)

*req = *req.WithContext(ctx)

Expand Down
22 changes: 11 additions & 11 deletions accesscontrol/jwt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -634,7 +634,7 @@ func Test_JWT_yields_permissions(t *testing.T) {
return
}

grantedPermissionsList, ok := req.Context().Value(request.BetaGrantedPermissions).([]string)
grantedPermissionsList, ok := req.Context().Value(request.GrantedPermissions).([]string)
if !ok {
subT.Errorf("Expected granted permissions within request context")
} else {
Expand Down Expand Up @@ -715,62 +715,62 @@ func TestJwtConfig(t *testing.T) {
"configuration error: myac: jwt key: read error: configured attribute and file",
},
{
"signature_algorithm, both beta_roles_map and beta_roles_map_file",
"signature_algorithm, both roles_map and roles_map_file",
`
server "test" {}
definitions {
jwt "myac" {
signature_algorithm = "HS256"
header = "..."
key = "..."
beta_roles_map = {}
beta_roles_map_file = "testdata/map.json"
roles_map = {}
roles_map_file = "testdata/map.json"
}
}
`,
"configuration error: myac: jwt roles map: read error: configured attribute and file",
},
{
"signature_algorithm, beta_roles_map_file not found",
"signature_algorithm, roles_map_file not found",
`
server "test" {}
definitions {
jwt "myac" {
signature_algorithm = "HS256"
header = "..."
key = "..."
beta_roles_map_file = "file_not_found"
roles_map_file = "file_not_found"
}
}
`,
"configuration error: myac: roles map: read error: open .*/testdata/file_not_found: no such file or directory",
},
{
"signature_algorithm, both beta_permissions_map and beta_permissions_map_file",
"signature_algorithm, both permissions_map and permissions_map_file",
`
server "test" {}
definitions {
jwt "myac" {
signature_algorithm = "HS256"
header = "..."
key = "..."
beta_permissions_map = {}
beta_permissions_map_file = "testdata/map.json"
permissions_map = {}
permissions_map_file = "testdata/map.json"
}
}
`,
"configuration error: myac: jwt permissions map: read error: configured attribute and file",
},
{
"signature_algorithm, beta_permissions_map_file not found",
"signature_algorithm, permissions_map_file not found",
`
server "test" {}
definitions {
jwt "myac" {
signature_algorithm = "HS256"
header = "..."
key = "..."
beta_permissions_map_file = "file_not_found"
permissions_map_file = "file_not_found"
}
}
`,
Expand Down
10 changes: 5 additions & 5 deletions accesscontrol/permissions.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func (r *requiredPermissions) getPermission(method string) (string, error) {

permission, exists := r.permissions[method]
if !exists {
return "", errors.MethodNotAllowed.Messagef("method %s not allowed by beta_required_permission", method)
return "", errors.MethodNotAllowed.Messagef("method %s not allowed by required_permission", method)
}
return permission, nil
}
Expand Down Expand Up @@ -93,18 +93,18 @@ func (p *PermissionsControl) Validate(req *http.Request) error {
}

ctx := req.Context()
ctx = context.WithValue(ctx, request.BetaRequiredPermission, requiredPermission)
ctx = context.WithValue(ctx, request.RequiredPermission, requiredPermission)
*req = *req.WithContext(ctx)

evalCtx := eval.ContextFromRequest(req)
*req = *req.WithContext(evalCtx.WithClientRequest(req))

grantedPermission, ok := ctx.Value(request.BetaGrantedPermissions).([]string)
grantedPermission, ok := ctx.Value(request.GrantedPermissions).([]string)
if !ok {
return errors.BetaInsufficientPermissions.Messagef("no permissions granted")
return errors.InsufficientPermissions.Messagef("no permissions granted")
}
if !hasGrantedPermission(grantedPermission, requiredPermission) {
return errors.BetaInsufficientPermissions.Messagef("required permission %q not granted", requiredPermission)
return errors.InsufficientPermissions.Messagef("required permission %q not granted", requiredPermission)
}
return nil
}
Expand Down
10 changes: 5 additions & 5 deletions accesscontrol/permissions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,15 +112,15 @@ func Test_PermissionsControl(t *testing.T) {
map[string]string{},
http.MethodGet,
nil,
"method not allowed error: method GET not allowed by beta_required_permission",
"method not allowed error: method GET not allowed by required_permission",
},
{
"no method permitted, permission granted",
"",
map[string]string{},
http.MethodPost,
[]string{"read"},
"method not allowed error: method POST not allowed by beta_required_permission",
"method not allowed error: method POST not allowed by required_permission",
},
{
"method permitted, no permission required, no permission granted",
Expand Down Expand Up @@ -184,15 +184,15 @@ func Test_PermissionsControl(t *testing.T) {
map[string]string{"*": "read"},
"BREW",
[]string{"read"},
"method not allowed error: method BREW not allowed by beta_required_permission",
"method not allowed error: method BREW not allowed by required_permission",
},
{
"standard method not allowed",
"",
map[string]string{http.MethodGet: ""},
http.MethodPost,
nil,
"method not allowed error: method POST not allowed by beta_required_permission",
"method not allowed error: method POST not allowed by required_permission",
},
{
"method permitted, permission required, no permissions granted",
Expand Down Expand Up @@ -256,7 +256,7 @@ func Test_PermissionsControl(t *testing.T) {
req := httptest.NewRequest(tt.method, "/", nil)
if tt.grantedPermissions != nil {
ctx := req.Context()
ctx = context.WithValue(ctx, request.BetaGrantedPermissions, tt.grantedPermissions)
ctx = context.WithValue(ctx, request.GrantedPermissions, tt.grantedPermissions)
*req = *req.WithContext(ctx)
}
err := pc.Validate(req)
Expand Down
12 changes: 6 additions & 6 deletions config/ac_jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ type JWT struct {
KeyFile string `hcl:"key_file,optional" docs:"Reference to file containing verification key. Mutually exclusive with {key}. See {key} for more information."`
Name string `hcl:"name,label"`
Remain hcl.Body `hcl:",remain"`
RolesClaim string `hcl:"beta_roles_claim,optional" docs:"Name of claim specifying the roles of the user represented by the token. The claim value must either be a string containing a space-separated list of role values or a list of string role values."`
RolesMap map[string][]string `hcl:"beta_roles_map,optional" docs:"Mapping of roles to granted permissions. Non-mapped roles can be assigned with {*} to specific permissions. Mutually exclusive with {beta_roles_map_file}."`
RolesMapFile string `hcl:"beta_roles_map_file,optional" docs:"Reference to JSON file containing role mappings. Mutually exclusive with {beta_roles_map}. See {beta_roles_map} for more information."`
PermissionsClaim string `hcl:"beta_permissions_claim,optional" docs:"Name of claim containing the granted permissions. The claim value must either be a string containing a space-separated list of permissions or a list of string permissions."`
PermissionsMap map[string][]string `hcl:"beta_permissions_map,optional" docs:"Mapping of granted permissions to additional granted permissions. Maps values from {beta_permissions_claim} and those created from {beta_roles_map}. The map is called recursively. Mutually exclusive with {beta_permissions_map_file}."`
PermissionsMapFile string `hcl:"beta_permissions_map_file,optional" docs:"Reference to JSON file containing permission mappings. Mutually exclusive with {beta_permissions_map}. See {beta_permissions_map} for more information."`
RolesClaim string `hcl:"roles_claim,optional" docs:"Name of claim specifying the roles of the user represented by the token. The claim value must either be a string containing a space-separated list of role values or a list of string role values."`
RolesMap map[string][]string `hcl:"roles_map,optional" docs:"Mapping of roles to granted permissions. Non-mapped roles can be assigned with {*} to specific permissions. Mutually exclusive with {roles_map_file}."`
RolesMapFile string `hcl:"roles_map_file,optional" docs:"Reference to JSON file containing role mappings. Mutually exclusive with {roles_map}. See {roles_map} for more information."`
PermissionsClaim string `hcl:"permissions_claim,optional" docs:"Name of claim containing the granted permissions. The claim value must either be a string containing a space-separated list of permissions or a list of string permissions."`
PermissionsMap map[string][]string `hcl:"permissions_map,optional" docs:"Mapping of granted permissions to additional granted permissions. Maps values from {permissions_claim} and those created from {roles_map}. The map is called recursively. Mutually exclusive with {permissions_map_file}."`
PermissionsMapFile string `hcl:"permissions_map_file,optional" docs:"Reference to JSON file containing permission mappings. Mutually exclusive with {permissions_map}. See {permissions_map} for more information."`
SignatureAlgorithm string `hcl:"signature_algorithm,optional" docs:"Valid values: {RS256}, {RS384}, {RS512}, {HS256}, {HS384}, {HS512}, {ES256}, {ES384}, {ES512}"`
SigningKey string `hcl:"signing_key,optional" docs:"Private key (in PEM format) for {RS*} and {ES*} variants. Mutually exclusive with {signing_key_file}."`
SigningKeyFile string `hcl:"signing_key_file,optional" docs:"Reference to file containing signing key. Mutually exclusive with {signing_key}. See {signing_key} for more information."`
Expand Down
2 changes: 1 addition & 1 deletion config/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func (a API) Inline() interface{} {
type Inline struct {
meta.ResponseHeadersAttributes
meta.LogFieldsAttribute
RequiredPermission hcl.Expression `hcl:"beta_required_permission,optional" docs:"Permission required to use this API (see [error type](/configuration/error-handling#error-types) {beta_insufficient_permissions})." type:"string or object (string)"`
RequiredPermission hcl.Expression `hcl:"required_permission,optional" docs:"Permission required to use this API (see [error type](/configuration/error-handling#error-types) {insufficient_permissions})." type:"string or object (string)"`
}

return &Inline{}
Expand Down
2 changes: 1 addition & 1 deletion config/configload/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func refineEndpoints(helper *helper, endpoints config.Endpoints, checkPathPatter
}
}

rp := endpointBody.Attributes["beta_required_permission"]
rp := endpointBody.Attributes["required_permission"]
if rp != nil {
ep.RequiredPermission = rp.Expr
}
Expand Down
2 changes: 1 addition & 1 deletion config/configload/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ func LoadConfig(body *hclsyntax.Body) (*config.Couper, error) {
return nil, err
}

rp := apiBody.Attributes["beta_required_permission"]
rp := apiBody.Attributes["required_permission"]
if rp != nil {
apiConfig.RequiredPermission = rp.Expr
}
Expand Down
2 changes: 1 addition & 1 deletion config/configload/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ func completeSchemaComponents(body hcl.Body, schema *hcl.BodySchema,

keyName := seetie.ValueToString(k)
switch name {
case "add_request_headers", "add_response_headers", "beta_required_permission", "headers", "set_request_headers", "set_response_headers":
case "add_request_headers", "add_response_headers", "required_permission", "headers", "set_request_headers", "set_response_headers":
// header field names, method names: handle object keys case-insensitively
keyName = strings.ToLower(keyName)
}
Expand Down
Loading