github.com/avenga/couper@v1.12.2/config/configload/helper.go (about) 1 package configload 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/hashicorp/hcl/v2" 8 "github.com/hashicorp/hcl/v2/gohcl" 9 "github.com/hashicorp/hcl/v2/hclsyntax" 10 11 "github.com/avenga/couper/config" 12 hclbody "github.com/avenga/couper/config/body" 13 "github.com/avenga/couper/config/sequence" 14 "github.com/avenga/couper/errors" 15 ) 16 17 type helper struct { 18 config *config.Couper 19 context *hcl.EvalContext 20 content *hcl.BodyContent 21 defsBackends map[string]*hclsyntax.Body 22 } 23 24 // newHelper creates a container with some methods to keep things simple here and there. 25 func newHelper(body hcl.Body) (*helper, error) { 26 couperConfig := &config.Couper{ 27 Context: evalContext, 28 Definitions: &config.Definitions{}, 29 Defaults: defaultsConfig, 30 Settings: config.NewDefaultSettings(), 31 } 32 33 schema, _ := gohcl.ImpliedBodySchema(couperConfig) 34 content, diags := body.Content(schema) 35 if content == nil { // reference diags only for missing content, due to optional server label 36 return nil, fmt.Errorf("invalid configuration: %w", diags) 37 } 38 39 return &helper{ 40 config: couperConfig, 41 content: content, 42 context: evalContext.HCLContext(), 43 defsBackends: make(map[string]*hclsyntax.Body), 44 }, nil 45 } 46 47 func (h *helper) addBackend(block *hcl.Block) { 48 name := block.Labels[0] 49 50 backendBody := block.Body.(*hclsyntax.Body) 51 setName(name, backendBody) 52 53 h.defsBackends[name] = backendBody 54 } 55 56 func (h *helper) configureDefinedBackends() error { 57 backendNames, err := h.resolveBackendDeps() 58 if err != nil { 59 return err 60 } 61 62 for _, name := range backendNames { 63 b, set := h.defsBackends[name] 64 if !set { 65 return errors.Configuration.Messagef("referenced backend %q is not defined", name) 66 } 67 be, err := PrepareBackend(h, "_init", "", &config.Backend{Name: name, Remain: b}) 68 if err != nil { 69 return err 70 } 71 h.config.Definitions.Backend = append( 72 h.config.Definitions.Backend, 73 &config.Backend{Remain: be, Name: name}, 74 ) 75 76 h.defsBackends[name] = be 77 } 78 return err 79 } 80 81 func (h *helper) configureACBackends() error { 82 var acs []config.BackendInitialization 83 for _, ac := range h.config.Definitions.JWT { 84 acs = append(acs, ac) 85 } 86 for _, ac := range h.config.Definitions.OAuth2AC { 87 acs = append(acs, ac) 88 } 89 90 for _, ac := range h.config.Definitions.OIDC { 91 acs = append(acs, ac) 92 } 93 94 for _, ac := range acs { 95 if err := ac.Prepare(func(attr string, attrVal string, b config.Body) (*hclsyntax.Body, error) { 96 return PrepareBackend(h, attr, attrVal, b) // wrap helper 97 }); err != nil { 98 return err 99 } 100 } 101 return nil 102 } 103 104 // resolveBackendDeps returns defined backends ordered by reference. Referenced ones need to be configured first. 105 func (h *helper) resolveBackendDeps() (uniqueItems []string, err error) { 106 // collect referenced backends 107 refs := make(map[string][]string) 108 h.collectBackendDeps(refs) 109 // built up deps 110 refPtr := map[string]*sequence.Item{} 111 for name := range refs { 112 parent := sequence.NewBackendItem(name) 113 refPtr[name] = parent 114 } 115 116 defer func() { 117 if p := recover(); p != nil { // since we use sequence related logic, replace wording due to backend context here 118 err = errors.Configuration.Message(strings.Replace(fmt.Sprintf("%s", p), "sequence ", "", 1)) 119 } 120 }() 121 122 var defs sequence.List 123 for parent, ref := range refs { 124 for _, r := range ref { 125 p := refPtr[parent] 126 if be, exist := refPtr[r]; exist { 127 p.Add(be) 128 } else { 129 p.Add(sequence.NewBackendItem(r)) 130 } 131 defs = append(defs, p) 132 } 133 } 134 135 items := sequence.Dependencies(defs) 136 137 // do not forget the other ones 138 var standalone []string 139 for def := range h.defsBackends { 140 standalone = append(standalone, def) 141 } 142 items = append(items, standalone) 143 144 // unique by name /w score (sort?) // TODO: MAY refine with scoring of appearance 145 unique := make(map[string]int) 146 for _, seqItem := range items { 147 for _, name := range seqItem { 148 if _, exist := unique[name]; !exist { 149 unique[name] = 1 150 uniqueItems = append(uniqueItems, name) 151 } else { 152 unique[name]++ 153 } 154 } 155 } 156 157 return uniqueItems, err 158 } 159 160 func (h *helper) collectBackendDeps(refs map[string][]string) { 161 for name, b := range h.defsBackends { 162 refs[name] = nil 163 oaBlocks := hclbody.BlocksOfType(b, oauth2) 164 h.collectFromBlocks(oaBlocks, name, refs) 165 trBlocks := hclbody.BlocksOfType(b, tokenRequest) 166 h.collectFromBlocks(trBlocks, name, refs) 167 } 168 } 169 170 func (h *helper) collectFromBlocks(authorizerBlocks hclsyntax.Blocks, name string, refs map[string][]string) { 171 for _, ab := range authorizerBlocks { 172 for _, be := range ab.Body.Attributes { 173 if be.Name == backend { 174 val, _ := be.Expr.Value(envContext) 175 refs[name] = append(refs[name], val.AsString()) 176 break 177 } 178 } 179 180 for _, block := range ab.Body.Blocks { 181 if block.Type != backend { 182 continue 183 } 184 if len(block.Labels) > 0 { 185 refs[name] = append(refs[name], block.Labels[0]) 186 } 187 188 for _, subBlock := range block.Body.Blocks { 189 switch subBlock.Type { 190 case oauth2, tokenRequest: 191 h.collectBackendDeps(refs) 192 } 193 } 194 } 195 } 196 }