github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/controller/pkg/auth/auth.go (about) 1 package auth 2 3 import ( 4 "context" 5 "crypto/x509" 6 "encoding/json" 7 "fmt" 8 "net/http" 9 "net/url" 10 "strings" 11 "sync" 12 13 "go.aporeto.io/enforcerd/trireme-lib/controller/pkg/secrets" 14 "go.aporeto.io/enforcerd/trireme-lib/controller/pkg/servicetokens" 15 "go.aporeto.io/enforcerd/trireme-lib/controller/pkg/urisearch" 16 "go.aporeto.io/enforcerd/trireme-lib/controller/pkg/usertokens" 17 "go.aporeto.io/enforcerd/trireme-lib/policy" 18 "go.uber.org/zap" 19 ) 20 21 // CallbackResponse captures all the response data of the call back processing. 22 type CallbackResponse struct { 23 Cookie *http.Cookie 24 Status int 25 OriginURL string 26 Data string 27 Message string 28 } 29 30 // Processor holds all the local data of the authorization engine. A processor 31 // can handle authorization for multiple services. The goal is to authenticate 32 // a request based on both service and user credentials. 33 type Processor struct { 34 apis *urisearch.APICache 35 userTokenHandler usertokens.Verifier 36 userTokenMappings map[string]string 37 userAuthorizationType policy.UserAuthorizationTypeValues 38 aporetoJWT *servicetokens.Verifier 39 sync.RWMutex 40 } 41 42 // NewProcessor creates an auth processor with PKI user tokens. The caller 43 // must provide a valid secrets structure and an optional list of trustedCertificates 44 // that can be used to validate tokens. If the list is empty, the CA from the secrets 45 // will be used for token validation. 46 func NewProcessor(s secrets.Secrets, trustedCertificate *x509.Certificate) *Processor { 47 return &Processor{ 48 aporetoJWT: servicetokens.NewVerifier(s, trustedCertificate), 49 } 50 } 51 52 // UpdateSecrets will update the Aporeto secrets for the validation of the 53 // Aporeto tokens. 54 func (p *Processor) UpdateSecrets(s secrets.Secrets, trustedCertificate *x509.Certificate) { 55 p.aporetoJWT.UpdateSecrets(s, trustedCertificate) 56 } 57 58 // AddOrUpdateService adds or replaces a service in the authorization db. 59 func (p *Processor) AddOrUpdateService(apis *urisearch.APICache, serviceType policy.UserAuthorizationTypeValues, handler usertokens.Verifier, mappings map[string]string) { 60 p.Lock() 61 defer p.Unlock() 62 63 p.apis = apis 64 p.userTokenMappings = mappings 65 p.userTokenHandler = handler 66 p.userAuthorizationType = serviceType 67 } 68 69 // UpdateServiceAPIs updates an existing service with a new API definition. 70 func (p *Processor) UpdateServiceAPIs(apis *urisearch.APICache) error { 71 p.Lock() 72 defer p.Unlock() 73 74 p.apis = apis 75 return nil 76 } 77 78 // DecodeUserClaims decodes the user claims with the user authorization method. 79 func (p *Processor) DecodeUserClaims(ctx context.Context, name, userToken string, certs []*x509.Certificate) ([]string, bool, string, error) { 80 81 switch p.userAuthorizationType { 82 case policy.UserAuthorizationMutualTLS, policy.UserAuthorizationJWT: 83 // First parse any incoming certificates and retrieve attributes from them. 84 // This is used in case of client authorization with certificates. 85 attributes := []string{} 86 for _, cert := range certs { 87 attributes = append(attributes, "CN="+cert.Subject.CommonName) 88 for _, email := range cert.EmailAddresses { 89 attributes = append(attributes, "Email="+email) 90 } 91 for _, org := range cert.Subject.Organization { 92 attributes = append(attributes, "O="+org) 93 } 94 for _, org := range cert.Subject.OrganizationalUnit { 95 attributes = append(attributes, "OU="+org) 96 } 97 } 98 99 if p.userAuthorizationType == policy.UserAuthorizationJWT && p.userTokenHandler != nil { 100 jwtAttributes, _, _, err := p.userTokenHandler.Validate(ctx, userToken) 101 if err != nil { 102 return attributes, false, userToken, fmt.Errorf("Unable to decode JWT: %s", err) 103 } 104 attributes = append(attributes, jwtAttributes...) 105 } 106 107 return attributes, false, userToken, nil 108 109 case policy.UserAuthorizationOIDC: 110 // Now we can parse the user claims. 111 if p.userTokenHandler == nil { 112 zap.L().Error("Internal Server Error: OIDC User Token Handler not configured") 113 return []string{}, false, userToken, nil 114 } 115 return p.userTokenHandler.Validate(ctx, userToken) 116 default: 117 return []string{}, false, userToken, nil 118 } 119 } 120 121 // DecodeAporetoClaims decodes the Aporeto claims 122 func (p *Processor) DecodeAporetoClaims(aporetoToken string, publicKey string) (string, []string, *policy.PingPayload, error) { 123 if len(aporetoToken) == 0 || p.aporetoJWT == nil { 124 return "", []string{}, nil, nil 125 } 126 127 // Finally we can parse the Aporeto token. 128 id, scopes, profile, pingPayload, err := p.aporetoJWT.ParseToken(aporetoToken, publicKey) 129 if err != nil { 130 return "", []string{}, nil, fmt.Errorf("Invalid Aporeto Token: %s", err) 131 } 132 133 return id, append(profile, scopes...), pingPayload, nil 134 } 135 136 // Callback is function called by and IDP auth provider will exchange the provided 137 // authorization code with a JWT token. This closes the Oauth loop. 138 func (p *Processor) Callback(ctx context.Context, u *url.URL) (*CallbackResponse, error) { 139 p.RLock() 140 defer p.RUnlock() 141 142 c := &CallbackResponse{} 143 144 // Validate the JWT token through the handler. 145 token, originURL, status, err := p.userTokenHandler.Callback(ctx, u) 146 if err != nil { 147 c.Status = http.StatusUnauthorized 148 c.Message = fmt.Sprintf("Invalid code %s:", err) 149 return c, err 150 } 151 152 c.OriginURL = originURL 153 c.Status = status 154 c.Cookie = &http.Cookie{ 155 Name: "X-APORETO-AUTH", 156 Value: token, 157 HttpOnly: true, 158 Secure: true, 159 Path: "/", 160 } 161 162 // We transmit the information in the return payload for applications 163 // that choose to use it directly without a cookie. 164 data, err := json.MarshalIndent(c.Cookie, " ", " ") 165 if err != nil { 166 c.Status = http.StatusInternalServerError 167 c.Message = "Unable to decode data" 168 return c, err 169 } 170 171 c.Data = string(data) 172 173 return c, nil 174 } 175 176 // Check is the main method that will search API cache and validate whether the call should 177 // be allowed. It returns two values. If the access is allowed, and whether the access 178 // public or not. This allows callers to decide what to do when there is a failure, and 179 // potentially issue a redirect. 180 func (p *Processor) Check(method, uri string, claims []string) (bool, bool) { 181 p.RLock() 182 defer p.RUnlock() 183 184 return p.apis.FindAndMatchScope(method, uri, claims) 185 } 186 187 // RedirectURI returns the redirect URI in order to start the authentication dance. 188 func (p *Processor) RedirectURI(originURL string) string { 189 p.RLock() 190 defer p.RUnlock() 191 192 return p.userTokenHandler.IssueRedirect(originURL) 193 } 194 195 // UpdateRequestHeaders will update the request headers based on the user claims 196 // and the corresponding mappings. 197 func (p *Processor) UpdateRequestHeaders(h http.Header, claims []string) { 198 p.RLock() 199 defer p.RUnlock() 200 201 if len(p.userTokenMappings) == 0 { 202 return 203 } 204 205 for _, claim := range claims { 206 parts := strings.SplitN(claim, "=", 2) 207 if header, ok := p.userTokenMappings[parts[0]]; ok && len(parts) == 2 { 208 h.Add(header, parts[1]) 209 } 210 } 211 } 212 213 // RetrieveServiceHandler will retrieve the service that is stored in the serviceMap 214 func (p *Processor) RetrieveServiceHandler() (usertokens.Verifier, error) { 215 return p.userTokenHandler, nil 216 }