...

Source file src/edge-infra.dev/pkg/edge/iam/verify/verifier_client.go

Documentation: edge-infra.dev/pkg/edge/iam/verify

     1  package verify
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"io"
     7  
     8  	http "net/http"
     9  	"net/url"
    10  
    11  	"github.com/MicahParks/keyfunc"
    12  	"github.com/gin-gonic/gin"
    13  	"github.com/golang-jwt/jwt/v4"
    14  
    15  	"edge-infra.dev/pkg/edge/iam/log"
    16  	"edge-infra.dev/pkg/edge/iam/util"
    17  	"edge-infra.dev/pkg/edge/iam/verify/templates"
    18  )
    19  
    20  const (
    21  	oauth2TokenPath    = "/oauth2/token" //nolint:gosec // Linter is not smart.
    22  	wellKnownJWKSPath  = "/.well-known/jwks.json"
    23  	verifyCallbackPath = "/verify/callback"
    24  )
    25  
    26  type TokenResponse struct {
    27  	AccessToken string `json:"access_token"`
    28  	TokenType   string `json:"token_type"`
    29  	ExpiresIn   int    `json:"expires_in"`
    30  	Scope       string `json:"scope"`
    31  }
    32  
    33  func (v *Verifier) client(ctx *gin.Context) {
    34  	log := log.Get(ctx.Request.Context())
    35  
    36  	result := &Result{
    37  		Name: "verify client result",
    38  		Pass: true,
    39  	}
    40  
    41  	// accessing jwks (will be used for parsing the JWT token)
    42  	jwksURL := IssuerURL() + wellKnownJWKSPath
    43  	jwks, err := keyfunc.Get(jwksURL, keyfunc.Options{})
    44  	if err != nil {
    45  		step := Step{Name: "load jwks", Pass: false}
    46  		writeResult(ctx, templates.Callback, result, step)
    47  		return
    48  	}
    49  
    50  	// setting the http body in x-www-form-urlencoded form
    51  	data := url.Values{}
    52  	data.Set("client_id", v.ClientID)
    53  	data.Set("client_secret", v.ClientSecret)
    54  	data.Set("grant_type", "client_credentials")
    55  
    56  	// Make a POST request to the token endpoint
    57  	res, err := http.Post(IssuerURL()+oauth2TokenPath, "application/x-www-form-urlencoded", bytes.NewBufferString(data.Encode()))
    58  	if err != nil {
    59  		step := Step{Name: "make request to token endpoint", Pass: false}
    60  		writeResult(ctx, templates.Callback, result, step)
    61  		return
    62  	}
    63  
    64  	defer res.Body.Close()
    65  
    66  	// Read the response body of token endpoint
    67  	body, err := io.ReadAll(res.Body)
    68  	if err != nil {
    69  		log.Error(err, "Error reading response")
    70  		return
    71  	}
    72  
    73  	// Unmarshal the response JSON into a TokenResponse struct
    74  	var tokenRes TokenResponse
    75  	err = json.Unmarshal(body, &tokenRes)
    76  	if err != nil {
    77  		log.Error(err, "Error unmarshaling response")
    78  		return
    79  	}
    80  
    81  	// parse and validate the access token
    82  	accessToken, _ := jwt.Parse(tokenRes.AccessToken, jwks.Keyfunc)
    83  
    84  	rolesOk := false
    85  	if accessToken.Valid {
    86  		// deserialse the claims
    87  		claims, _ := accessToken.Claims.(jwt.MapClaims)
    88  		serialisedRls, _ := claims["rls"].(string)
    89  		roles, _ := util.Deserialize(serialisedRls)
    90  
    91  		// check if the role contains SELLING_EXECUTE
    92  		for _, role := range roles {
    93  			if role == "SELLING_EXECUTE" {
    94  				rolesOk = true
    95  				break
    96  			}
    97  		}
    98  	}
    99  
   100  	steps := make([]Step, 0)
   101  	steps = append(steps,
   102  		Step{Name: "valid access token", Pass: accessToken.Valid},
   103  		Step{Name: "roles include SELLING_EXECUTE", Pass: rolesOk},
   104  	)
   105  
   106  	writeResult(ctx, templates.Client, result, steps...)
   107  }
   108  

View as plain text