github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/pkg/graphql/auth.go (about)

     1  package graphql
     2  
     3  import (
     4  	"encoding/json"
     5  	"reflect"
     6  )
     7  
     8  type credential struct {
     9  	*BasicCredentialData
    10  	*OAuthCredentialData
    11  	*CertificateOAuthCredentialData
    12  }
    13  
    14  // OneTimeTokenDTO this a model for transportation of one-time tokens, because the json marshaller cannot unmarshal to either of the types OTTForApp or OTTForRuntime
    15  type OneTimeTokenDTO struct {
    16  	TokenWithURL
    17  	LegacyConnectorURL string `json:"legacyConnectorURL"`
    18  }
    19  
    20  // oneTimeTokenDTO is used to hyde TypeName property to consumers
    21  type oneTimeTokenDTO struct {
    22  	*OneTimeTokenDTO
    23  	TypeName string `json:"__typename"`
    24  }
    25  
    26  // IsOneTimeToken implements the interface OneTimeToken
    27  func (*OneTimeTokenDTO) IsOneTimeToken() {}
    28  
    29  // UnmarshalJSON is used only by integration tests, we have to help graphql client to deal with Credential field
    30  func (a *Auth) UnmarshalJSON(data []byte) error {
    31  	type Alias Auth
    32  
    33  	aux := &struct {
    34  		*Alias
    35  		Credential   credential       `json:"credential"`
    36  		OneTimeToken *oneTimeTokenDTO `json:"oneTimeToken"`
    37  	}{
    38  		Alias: (*Alias)(a),
    39  	}
    40  	if err := json.Unmarshal(data, &aux); err != nil {
    41  		return err
    42  	}
    43  
    44  	cred, err := retrieveCredential(data)
    45  	if err != nil {
    46  		return err
    47  	}
    48  
    49  	a.Credential = cred
    50  
    51  	if aux.OneTimeToken != nil {
    52  		a.OneTimeToken = retrieveOneTimeToken(aux.OneTimeToken)
    53  	}
    54  
    55  	return nil
    56  }
    57  
    58  // UnmarshalJSON missing godoc
    59  func (csrf *CSRFTokenCredentialRequestAuth) UnmarshalJSON(data []byte) error {
    60  	type Alias CSRFTokenCredentialRequestAuth
    61  
    62  	aux := &struct {
    63  		*Alias
    64  		Credential credential `json:"credential"`
    65  	}{
    66  		Alias: (*Alias)(csrf),
    67  	}
    68  
    69  	if err := json.Unmarshal(data, &aux); err != nil {
    70  		return err
    71  	}
    72  
    73  	cred, err := retrieveCredential(data)
    74  	if err != nil {
    75  		return err
    76  	}
    77  
    78  	csrf.Credential = cred
    79  
    80  	return nil
    81  }
    82  
    83  // retrieveCredential checks if any of the structs BasicCredentialData, OAuthCredentialData or CertificateOAuthCredentialData
    84  // has all the data after unmarshalling. The resulting CredentialData is the one struct that has all it's fields full of data
    85  // This is done because CredentialData is an interface and the structs that implement it have conflicting json tag names, so
    86  // they could not be marshalled properly
    87  func retrieveCredential(data []byte) (CredentialData, error) {
    88  	var basicCredential struct {
    89  		BasicCredentialData `json:"credential"`
    90  	}
    91  	if err := json.Unmarshal(data, &basicCredential); err != nil {
    92  		return nil, err
    93  	}
    94  
    95  	if isBasic := isCredentialStructFullWithData(basicCredential.BasicCredentialData); isBasic {
    96  		return &basicCredential.BasicCredentialData, nil
    97  	}
    98  
    99  	var oauthCredential struct {
   100  		OAuthCredentialData `json:"credential"`
   101  	}
   102  	if err := json.Unmarshal(data, &oauthCredential); err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	if isOAuth := isCredentialStructFullWithData(oauthCredential.OAuthCredentialData); isOAuth {
   107  		return &oauthCredential.OAuthCredentialData, nil
   108  	}
   109  
   110  	var certOAuthCredential struct {
   111  		CertificateOAuthCredentialData `json:"credential"`
   112  	}
   113  	if err := json.Unmarshal(data, &certOAuthCredential); err != nil {
   114  		return nil, err
   115  	}
   116  
   117  	if isCertificateOAuth := isCredentialStructFullWithData(certOAuthCredential.CertificateOAuthCredentialData); isCertificateOAuth {
   118  		return &certOAuthCredential.CertificateOAuthCredentialData, nil
   119  	}
   120  
   121  	return nil, nil
   122  }
   123  
   124  // isCredentialStructFullWithData checks if any of the fields in the struct is same as an empty struct property or is nil and
   125  // if this tests positive then it is considered that the struct is not in a valid form will properties full of data
   126  func isCredentialStructFullWithData(obj interface{}) bool {
   127  	if obj == nil {
   128  		return false
   129  	}
   130  
   131  	v := reflect.ValueOf(obj)
   132  	for i := 0; i < v.NumField(); i++ {
   133  		empty := reflect.New(v.Field(i).Type()).Elem().Interface()
   134  		value := v.Field(i).Interface()
   135  		if reflect.DeepEqual(value, empty) || v.Field(i).Interface() == nil {
   136  			return false
   137  		}
   138  	}
   139  
   140  	return true
   141  }
   142  
   143  func retrieveOneTimeToken(ottDTO *oneTimeTokenDTO) OneTimeToken {
   144  	switch ottDTO.TypeName {
   145  	case "OneTimeTokenForApplication":
   146  		return &OneTimeTokenForApplication{
   147  			TokenWithURL:       ottDTO.TokenWithURL,
   148  			LegacyConnectorURL: ottDTO.LegacyConnectorURL,
   149  		}
   150  	case "OneTimeTokenForRuntime":
   151  		return &OneTimeTokenForRuntime{
   152  			TokenWithURL: ottDTO.TokenWithURL,
   153  		}
   154  	}
   155  	return ottDTO.OneTimeTokenDTO
   156  }