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 }