* @brief Enforces access control, currently supports access token from a cookie. * * @param instance plugin instance pointer * @param txn transaction handle * @param rri remap request info pointer * @param config pointer to the plugin configuration * @return TSREMAP_NO_REMAP (access validation = success) * TSREMAP_DID_REMAP (access validation = failure and rejection of failed requests is co
| 486 | * TSREMAP_DID_REMAP (access validation = failure and rejection of failed requests is configured) |
| 487 | */ |
| 488 | TSRemapStatus |
| 489 | enforceAccessControl(TSHttpTxn txnp, TSRemapRequestInfo *rri, AccessControlConfig *config) |
| 490 | { |
| 491 | if (config->_cookieName.empty()) { |
| 492 | /* For now only checking a cookie is supported and if its name is unknown (checking cookie disabled) then do nothing. */ |
| 493 | return TSREMAP_NO_REMAP; |
| 494 | } |
| 495 | |
| 496 | TSRemapStatus remapStatus = TSREMAP_NO_REMAP; |
| 497 | |
| 498 | /* Create txn data and register hooks */ |
| 499 | AccessControlTxnData *data = new AccessControlTxnData(config); |
| 500 | TSCont cont = TSContCreate(contHandleAccessControl, TSMutexCreate()); |
| 501 | TSContDataSet(cont, static_cast<void *>(data)); |
| 502 | TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, cont); |
| 503 | TSHttpTxnHookAdd(txnp, TS_HTTP_TXN_CLOSE_HOOK, cont); |
| 504 | |
| 505 | /* Validate the token */ |
| 506 | bool reject = config->_rejectRequestsWithInvalidTokens; |
| 507 | String cookie; |
| 508 | bool found = getCookieByName(txnp, rri->requestBufp, rri->requestHdrp, config->_cookieName, cookie); |
| 509 | if (found) { |
| 510 | AccessControlDebug("%s cookie: '%s'", config->_cookieName.c_str(), cookie.c_str()); |
| 511 | |
| 512 | /* |
| 513 | * From RFC 6265 "HTTP State Management Mechanism": |
| 514 | * To maximize compatibility with user agents, servers that wish to |
| 515 | * store arbitrary data in a cookie-value SHOULD encode that data, for |
| 516 | * example, using Base64 [RFC4648]. |
| 517 | */ |
| 518 | size_t decodedCookieBufferSize = cryptoBase64DecodeSize(cookie.c_str(), cookie.size()); |
| 519 | char decodedCookie[decodedCookieBufferSize]; |
| 520 | size_t decryptedCookieSize = cryptoModifiedBase64Decode(cookie.c_str(), cookie.size(), decodedCookie, decodedCookieBufferSize); |
| 521 | if (0 < decryptedCookieSize) { |
| 522 | AccessToken *token = config->_tokenFactory->getAccessToken(); |
| 523 | if (nullptr != token) { |
| 524 | data->_vaState = token->validate(StringView(decodedCookie, decryptedCookieSize), time(nullptr)); |
| 525 | if (VALID != data->_vaState) { |
| 526 | remapStatus = |
| 527 | handleInvalidToken(txnp, data, reject, accessTokenStateToHttpStatus(data->_vaState, config), data->_vaState); |
| 528 | } else { |
| 529 | /* Valid token, if configured extract the token subject to a header, |
| 530 | * only if we can trust it - token is valid to prevent using it by mistake */ |
| 531 | if (!config->_extrSubHdrName.empty()) { |
| 532 | String sub(token->getSubject()); |
| 533 | setHeader(rri->requestBufp, rri->requestHdrp, config->_extrSubHdrName.c_str(), config->_extrSubHdrName.size(), |
| 534 | sub.c_str(), sub.size()); |
| 535 | } |
| 536 | } |
| 537 | /* If configure extract the UA token id into a header likely for debugging, |
| 538 | * extract it even if token validation fails and we don't trust it */ |
| 539 | if (!config->_extrTokenIdHdrName.empty()) { |
| 540 | String tokeId(token->getTokenId()); |
| 541 | setHeader(rri->requestBufp, rri->requestHdrp, config->_extrTokenIdHdrName.c_str(), config->_extrTokenIdHdrName.size(), |
| 542 | tokeId.c_str(), tokeId.size()); |
| 543 | } |
| 544 | delete token; |
| 545 | } else { |
no test coverage detected