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

Missing referenced blocks #651

Merged
merged 9 commits into from
Jan 19, 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Unreleased changes are available as `avenga/couper:edge` container.
* **Changed**
* 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))

---

Expand Down
8 changes: 7 additions & 1 deletion config/configload/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func newCatchAllEndpoint() *config.Endpoint {
}
}

func refineEndpoints(helper *helper, endpoints config.Endpoints, checkPathPattern bool) error {
func refineEndpoints(helper *helper, endpoints config.Endpoints, checkPathPattern bool, definedACs map[string]struct{}) error {
var err error

for _, ep := range endpoints {
Expand All @@ -38,6 +38,12 @@ func refineEndpoints(helper *helper, endpoints config.Endpoints, checkPathPatter
}

endpointBody := ep.HCLBody()
if definedACs != nil {
if err := checkReferencedAccessControls(endpointBody, ep.AccessControl, ep.DisableAccessControl, definedACs); err != nil {
return err
}
}

rp := endpointBody.Attributes["beta_required_permission"]
if rp != nil {
ep.RequiredPermission = rp.Expr
Expand Down
2 changes: 1 addition & 1 deletion config/configload/endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
)

func Test_refineEndpoints_noPattern(t *testing.T) {
err := refineEndpoints(nil, config.Endpoints{{Pattern: ""}}, true)
err := refineEndpoints(nil, config.Endpoints{{Pattern: ""}}, true, nil)
if err == nil || !strings.HasSuffix(err.Error(), "endpoint: missing path pattern; ") {
t.Errorf("refineEndpoints() error = %v, wantErr: endpoint: missing path pattern ", err)
}
Expand Down
2 changes: 1 addition & 1 deletion config/configload/error_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ func newErrorHandlerConfig(content kindContent, helper *helper) (*config.ErrorHa
Requests: errHandlerConf.Requests,
}

if err := refineEndpoints(helper, config.Endpoints{ep}, false); err != nil {
if err := refineEndpoints(helper, config.Endpoints{ep}, false, nil); err != nil {
return nil, err
}

Expand Down
43 changes: 40 additions & 3 deletions config/configload/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,23 @@ func LoadConfig(body *hclsyntax.Body) (*config.Couper, error) {
WithOAuth2AC(helper.config.Definitions.OAuth2AC).
WithSAML(helper.config.Definitions.SAML)

definedACs := make(map[string]struct{})
for _, ac := range helper.config.Definitions.BasicAuth {
definedACs[ac.Name] = struct{}{}
}
for _, ac := range helper.config.Definitions.JWT {
definedACs[ac.Name] = struct{}{}
}
for _, ac := range helper.config.Definitions.OAuth2AC {
definedACs[ac.Name] = struct{}{}
}
for _, ac := range helper.config.Definitions.OIDC {
definedACs[ac.Name] = struct{}{}
}
for _, ac := range helper.config.Definitions.SAML {
definedACs[ac.Name] = struct{}{}
}

// Read per server block and merge backend settings which results in a final server configuration.
for _, serverBlock := range hclbody.BlocksOfType(body, server) {
serverConfig := &config.Server{}
Expand All @@ -380,6 +397,22 @@ func LoadConfig(body *hclsyntax.Body) (*config.Couper, error) {
serverConfig.Name = serverBlock.Labels[0]
}

if err := checkReferencedAccessControls(serverBlock.Body, serverConfig.AccessControl, serverConfig.DisableAccessControl, definedACs); err != nil {
return nil, err
}

for _, fileConfig := range serverConfig.Files {
if err := checkReferencedAccessControls(fileConfig.HCLBody(), fileConfig.AccessControl, fileConfig.DisableAccessControl, definedACs); err != nil {
return nil, err
}
}

for _, spaConfig := range serverConfig.SPAs {
if err := checkReferencedAccessControls(spaConfig.HCLBody(), spaConfig.AccessControl, spaConfig.DisableAccessControl, definedACs); err != nil {
return nil, err
}
}

// Read api blocks and merge backends with server and definitions backends.
for _, apiConfig := range serverConfig.APIs {
apiBody := apiConfig.HCLBody()
Expand All @@ -390,12 +423,16 @@ func LoadConfig(body *hclsyntax.Body) (*config.Couper, error) {
}
}

if err := checkReferencedAccessControls(apiBody, apiConfig.AccessControl, apiConfig.DisableAccessControl, definedACs); err != nil {
return nil, err
}

rp := apiBody.Attributes["beta_required_permission"]
if rp != nil {
apiConfig.RequiredPermission = rp.Expr
}

err = refineEndpoints(helper, apiConfig.Endpoints, true)
err = refineEndpoints(helper, apiConfig.Endpoints, true, definedACs)
if err != nil {
return nil, err
}
Expand All @@ -414,7 +451,7 @@ func LoadConfig(body *hclsyntax.Body) (*config.Couper, error) {
}

// standalone endpoints
err = refineEndpoints(helper, serverConfig.Endpoints, true)
err = refineEndpoints(helper, serverConfig.Endpoints, true, definedACs)
if err != nil {
return nil, err
}
Expand All @@ -439,7 +476,7 @@ func LoadConfig(body *hclsyntax.Body) (*config.Couper, error) {
Requests: job.Requests,
}

err = refineEndpoints(helper, config.Endpoints{endpointConf}, false)
err = refineEndpoints(helper, config.Endpoints{endpointConf}, false, nil)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion config/configload/merge.go
Original file line number Diff line number Diff line change
Expand Up @@ -767,7 +767,7 @@ func addProxy(block *hclsyntax.Block, proxies map[string]*hclsyntax.Block) error
if !ok {
sr := attr.Expr.StartRange()

return newDiagErr(&sr, "proxy reference is not defined")
return newDiagErr(&sr, fmt.Sprintf("referenced proxy %q is not defined", reference))
}

delete(block.Body.Attributes, proxy)
Expand Down
23 changes: 23 additions & 0 deletions config/configload/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -330,3 +330,26 @@ func validateBackendTLS(block *hclsyntax.Block) error {
}
return nil
}

func checkReferencedAccessControls(body *hclsyntax.Body, acs, dacs []string, definedACs map[string]struct{}) error {
for _, ac := range acs {
if ac = strings.TrimSpace(ac); ac == "" {
continue
}
if _, set := definedACs[ac]; !set {
r := body.Attributes["access_control"].Expr.Range()
return newDiagErr(&r, fmt.Sprintf("referenced access control %q is not defined", ac))
}
}
for _, ac := range dacs {
if ac = strings.TrimSpace(ac); ac == "" {
alex-schneider marked this conversation as resolved.
Show resolved Hide resolved
continue
}
if _, set := definedACs[ac]; !set {
r := body.Attributes["disable_access_control"].Expr.Range()
return newDiagErr(&r, fmt.Sprintf("referenced access control %q is not defined", ac))
}
}

return nil
}
127 changes: 127 additions & 0 deletions config/configload/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1407,6 +1407,11 @@ func TestPermissionMixed(t *testing.T) {
response {}
}
}
}
definitions {
basic_auth "foo" {
password = "asdf"
}
}`,
"",
},
Expand Down Expand Up @@ -1522,3 +1527,125 @@ definitions {
})
}
}

func Test_checkReferencedAccessControls(t *testing.T) {
tests := []struct {
name string
hcl string
error string
}{
{
"missing AC referenced by server access_control",
`server {
access_control = ["undefined"]
}`,
`couper.hcl:2,23-36: referenced access control "undefined" is not defined; `,
},
{
"missing AC referenced by server disable_access_control",
`server {
disable_access_control = ["undefined"]
}`,
`couper.hcl:2,31-44: referenced access control "undefined" is not defined; `,
},
{
"missing AC referenced by api access_control",
`server {
api {
access_control = ["undefined"]
}
}`,
`couper.hcl:3,25-38: referenced access control "undefined" is not defined; `,
},
{
"missing AC referenced by api disable_access_control",
`server {
api {
disable_access_control = ["undefined"]
}
}`,
`couper.hcl:3,33-46: referenced access control "undefined" is not defined; `,
},
{
"missing AC referenced by files access_control",
`server {
files {
access_control = ["undefined"]
document_root = "htdocs"
}
}`,
`couper.hcl:3,25-38: referenced access control "undefined" is not defined; `,
},
{
"missing AC referenced by files disable_access_control",
`server {
files {
disable_access_control = ["undefined"]
document_root = "htdocs"
}
}`,
`couper.hcl:3,33-46: referenced access control "undefined" is not defined; `,
},
{
"missing AC referenced by spa access_control",
`server {
spa {
access_control = ["undefined"]
bootstrap_file = "foo"
paths = ["/**"]
}
}`,
`couper.hcl:3,25-38: referenced access control "undefined" is not defined; `,
},
{
"missing AC referenced by spa disable_access_control",
`server {
spa {
disable_access_control = ["undefined"]
bootstrap_file = "foo"
paths = ["/**"]
}
}`,
`couper.hcl:3,33-46: referenced access control "undefined" is not defined; `,
},
{
"missing AC referenced by endpoint access_control",
`server {
endpoint "/" {
access_control = ["undefined"]
response {
body = "OK"
}
}
}`,
`couper.hcl:3,25-38: referenced access control "undefined" is not defined; `,
},
{
"missing AC referenced by endpoint disable_access_control",
`server {
endpoint "/" {
disable_access_control = ["undefined"]
response {
body = "OK"
}
}
}`,
`couper.hcl:3,33-46: referenced access control "undefined" is not defined; `,
},
}

for _, tt := range tests {
t.Run(tt.name, func(subT *testing.T) {
_, err := LoadBytes([]byte(tt.hcl), "couper.hcl")

var errMsg string
if err != nil {
errMsg = err.Error()
}

if tt.error != errMsg {
subT.Errorf("%q: Unexpected configuration error:\n\tWant: %q\n\tGot: %q", tt.name, tt.error, errMsg)
}
})
}
}
6 changes: 6 additions & 0 deletions config/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package config
import (
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/gohcl"
"github.com/hashicorp/hcl/v2/hclsyntax"

"github.com/avenga/couper/config/meta"
)
Expand All @@ -23,6 +24,11 @@ type Files struct {
Remain hcl.Body `hcl:",remain"`
}

// HCLBody implements the <Body> interface.
func (f Files) HCLBody() *hclsyntax.Body {
return f.Remain.(*hclsyntax.Body)
}

// Inline implements the <Inline> interface.
func (f Files) Inline() interface{} {
type Inline struct {
Expand Down
13 changes: 0 additions & 13 deletions config/runtime/access_control.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package runtime

import (
"fmt"
"strings"

"github.com/avenga/couper/accesscontrol"
Expand All @@ -22,15 +21,3 @@ func (m ACDefinitions) Add(name string, ac accesscontrol.AccessControl, eh []*co
ErrorHandler: eh,
}
}

func (m ACDefinitions) MustExist(name string) error {
if m == nil {
panic("no accessControl configuration")
}

if _, ok := m[name]; !ok {
return fmt.Errorf("accessControl is not defined: " + name)
}

return nil
}
10 changes: 1 addition & 9 deletions config/runtime/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -533,12 +533,7 @@ func configureAccessControls(conf *config.Couper, confCtx *hcl.EvalContext, log

for _, saml := range conf.Definitions.SAML {
confErr := errors.Configuration.Label(saml.Name)
metadata, err := reader.ReadFromFile("saml2 idp_metadata_file", saml.IdpMetadataFile)
if err != nil {
return nil, confErr.With(err)
}

s, err := ac.NewSAML2ACS(metadata, saml.Name, saml.SpAcsURL, saml.SpEntityID, saml.ArrayAttributes)
s, err := ac.NewSAML2ACS(saml.MetadataBytes, saml.Name, saml.SpAcsURL, saml.SpEntityID, saml.ArrayAttributes)
if err != nil {
return nil, confErr.With(err)
}
Expand Down Expand Up @@ -651,9 +646,6 @@ func configureProtectedHandler(m ACDefinitions, conf *config.Couper, ctx *hcl.Ev
opts *protectedOptions, log *logrus.Entry) (http.Handler, error) {
var list ac.List
for _, acName := range parentAC.Merge(handlerAC).List() {
if e := m.MustExist(acName); e != nil {
return nil, e
}
eh, err := newErrorHandler(ctx, conf, opts, log, m, acName)
if err != nil {
return nil, err
Expand Down
10 changes: 3 additions & 7 deletions eval/lib/jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,14 +148,10 @@ func NewJwtSignFunction(ctx *hcl.EvalContext, jwtSigningConfigs map[string]*JWTS
},
Type: function.StaticReturnType(cty.String),
Impl: func(args []cty.Value, _ cty.Type) (ret cty.Value, err error) {
if len(jwtSigningConfigs) == 0 {
return cty.StringVal(""), fmt.Errorf("missing jwt_signing_profile or jwt (with signing_ttl) definitions")
}

label := args[0].AsString()
signingConfig := jwtSigningConfigs[label]
if signingConfig == nil {
return cty.StringVal(""), fmt.Errorf("missing jwt_signing_profile or jwt (with signing_ttl) for given label %q", label)
signingConfig, exist := jwtSigningConfigs[label]
if !exist {
return cty.StringVal(""), fmt.Errorf("missing jwt_signing_profile or jwt (with signing_ttl) block with referenced label %q", label)
}

var claims, argumentClaims, headers map[string]interface{}
Expand Down
Loading