XRootD
Loading...
Searching...
No Matches
XrdAccSciTokens Class Reference
Inheritance diagram for XrdAccSciTokens:
Collaboration diagram for XrdAccSciTokens:

Public Member Functions

 XrdAccSciTokens (XrdSysLogger *lp, const char *parms, XrdAccAuthorize *chain, XrdOucEnv *envP)
virtual ~XrdAccSciTokens ()
virtual XrdAccPrivs Access (const XrdSecEntity *Entity, const char *path, const Access_Operation oper, XrdOucEnv *env) override
virtual int Audit (const int accok, const XrdSecEntity *Entity, const char *path, const Access_Operation oper, XrdOucEnv *Env=0) override
std::string GetConfigFile ()
virtual Issuers IssuerList () override
virtual int Test (const XrdAccPrivs priv, const Access_Operation oper) override
virtual bool Validate (const char *token, std::string &emsg, long long *expT, XrdSecEntity *Entity) override
Public Member Functions inherited from XrdAccAuthorize
 XrdAccAuthorize ()
 Constructor.
virtual ~XrdAccAuthorize ()
 Destructor.
virtual XrdAccPrivs Access (const XrdSecEntity *Entity, const char *path, const Access_Operation oper, std::string &eInfo, XrdOucEnv *Env=0)
Public Member Functions inherited from XrdSciTokensHelper
 XrdSciTokensHelper ()
 Constructor and Destructor.
virtual ~XrdSciTokensHelper ()
Public Member Functions inherited from XrdSciTokensMon
 XrdSciTokensMon ()
 ~XrdSciTokensMon ()
bool Mon_isIO (const Access_Operation oper)
void Mon_Report (const XrdSecEntity &Entity, const std::string &subject, const std::string &username)

Additional Inherited Members

Public Types inherited from XrdSciTokensHelper
typedef std::vector< ValidIssuerIssuers

Detailed Description

Definition at line 465 of file XrdSciTokensAccess.cc.

Constructor & Destructor Documentation

◆ XrdAccSciTokens()

XrdAccSciTokens::XrdAccSciTokens ( XrdSysLogger * lp,
const char * parms,
XrdAccAuthorize * chain,
XrdOucEnv * envP )
inline

Definition at line 476 of file XrdSciTokensAccess.cc.

476 :
477 m_chain(chain),
478 m_parms(parms ? parms : ""),
479 m_next_clean(monotonic_time() + m_expiry_secs),
480 m_log(lp, "scitokens_")
481 {
482 pthread_rwlock_init(&m_config_lock, nullptr);
483 m_config_lock_initialized = true;
484 m_log.Say("++++++ XrdAccSciTokens: Initialized SciTokens-based authorization.");
485 if (!Config(envP)) {
486 throw std::runtime_error("Failed to configure SciTokens authorization.");
487 }
488 }

References XrdAccAuthorize::XrdAccAuthorize().

Here is the call graph for this function:

◆ ~XrdAccSciTokens()

virtual XrdAccSciTokens::~XrdAccSciTokens ( )
inlinevirtual

Definition at line 490 of file XrdSciTokensAccess.cc.

490 {
491 if (m_config_lock_initialized) {
492 pthread_rwlock_destroy(&m_config_lock);
493 }
494 }

Member Function Documentation

◆ Access()

virtual XrdAccPrivs XrdAccSciTokens::Access ( const XrdSecEntity * Entity,
const char * path,
const Access_Operation oper,
XrdOucEnv * Env )
inlineoverridevirtual

Check whether or not the client is permitted specified access to a path.

Parameters
Entity-> Authentication information
path-> The logical path which is the target of oper
oper-> The operation being attempted (see the enum above). If the oper is AOP_Any, then the actual privileges are returned and the caller may make subsequent tests using Test().
Env-> Environmental information at the time of the operation as supplied by the path CGI string. This is optional and the pointer may be zero.
Returns
Permit: a non-zero value (access is permitted) Deny: zero (access is denied)

Implements XrdAccAuthorize.

Definition at line 496 of file XrdSciTokensAccess.cc.

500 {
501 const char *authz = env ? env->Get("authz") : nullptr;
502 // Note: this is more permissive than the plugin was previously.
503 // The prefix 'Bearer%20' used to be required as that's what HTTP
504 // required. However, to make this more pleasant for XRootD protocol
505 // users, we now simply "handle" the prefix insterad of requiring it.
506 if (authz && !strncmp(authz, "Bearer%20", 9)) {
507 authz += 9;
508 }
509 // If there's no request-specific token, then see if the ZTN authorization
510 // has provided us with a session token.
511 if (!authz && Entity && !strcmp("ztn", Entity->prot) && Entity->creds &&
512 Entity->credslen && Entity->creds[Entity->credslen] == '\0')
513 {
514 authz = Entity->creds;
515 }
516 if (authz == nullptr) {
517 return OnMissing(Entity, path, oper, env);
518 }
519 m_log.Log(LogMask::Debug, "Access", "Trying token-based access control");
520 std::shared_ptr<XrdAccRules> access_rules;
521 uint64_t now = monotonic_time();
522 Check(now);
523 {
524 std::lock_guard<std::mutex> guard(m_map_mutex);
525 const auto iter = m_map.find(authz);
526 if (iter != m_map.end() && !iter->second->expired()) {
527 access_rules = iter->second;
528 }
529 }
530 if (!access_rules) {
531 m_log.Log(LogMask::Debug, "Access", "Token not found in recent cache; parsing.");
532 try {
533 uint64_t cache_expiry;
534 AccessRulesRaw rules;
535 std::string username;
536 std::string token_subject;
537 std::string issuer;
538 std::vector<MapRule> map_rules;
539 std::vector<std::string> groups;
540 uint32_t authz_strategy;
541 if (GenerateAcls(authz, cache_expiry, rules, username, token_subject, issuer, map_rules, groups, authz_strategy)) {
542 access_rules.reset(new XrdAccRules(now + cache_expiry, username, token_subject, issuer, map_rules, groups, authz_strategy));
543 access_rules->parse(rules);
544 } else {
545 m_log.Log(LogMask::Warning, "Access", "Failed to generate ACLs for token");
546 return OnMissing(Entity, path, oper, env);
547 }
548 if (m_log.getMsgMask() & LogMask::Debug) {
549 m_log.Log(LogMask::Debug, "Access", "New valid token", access_rules->str().c_str());
550 }
551 } catch (std::exception &exc) {
552 m_log.Log(LogMask::Warning, "Access", "Error generating ACLs for authorization", exc.what());
553 return OnMissing(Entity, path, oper, env);
554 }
555 std::lock_guard<std::mutex> guard(m_map_mutex);
556 m_map[authz] = access_rules;
557 } else if (m_log.getMsgMask() & LogMask::Debug) {
558 m_log.Log(LogMask::Debug, "Access", "Cached token", access_rules->str().c_str());
559 }
560
561 // Strategy: assuming the corresponding strategy is enabled, we populate the name in
562 // the XrdSecEntity if:
563 // 1. There are scopes present in the token that authorize the request,
564 // 2. The token is mapped by some rule in the mapfile (group or subject-based mapping).
565 // The default username for the issuer is only used in (1).
566 // If the scope-based mapping is successful, authorize immediately. Otherwise, if the
567 // mapping is successful, we potentially chain to another plugin.
568 //
569 // We always populate the issuer and the groups, if present.
570
571 // Access may be authorized; populate XrdSecEntity
572 XrdSecEntity new_secentity;
573 new_secentity.vorg = nullptr;
574 new_secentity.grps = nullptr;
575 new_secentity.role = nullptr;
576 new_secentity.secMon = Entity->secMon;
577 new_secentity.addrInfo = Entity->addrInfo;
578 const auto &issuer = access_rules->get_issuer();
579 if (!issuer.empty()) {
580 new_secentity.vorg = strdup(issuer.c_str());
581 }
582 bool group_success = false;
583 if ((access_rules->get_authz_strategy() & IssuerAuthz::Group) && access_rules->groups().size()) {
584 std::stringstream ss;
585 for (const auto &grp : access_rules->groups()) {
586 ss << grp << " ";
587 }
588 const auto &groups_str = ss.str();
589 new_secentity.grps = static_cast<char*>(malloc(groups_str.size() + 1));
590 if (new_secentity.grps) {
591 memcpy(new_secentity.grps, groups_str.c_str(), groups_str.size());
592 new_secentity.grps[groups_str.size()] = '\0';
593 }
594 group_success = true;
595 }
596
597 std::string username;
598 bool mapping_success = false;
599 bool scope_success = false;
600 username = access_rules->get_username(path);
601
602 mapping_success = (access_rules->get_authz_strategy() & IssuerAuthz::Mapping) && !username.empty();
603 scope_success = (access_rules->get_authz_strategy() & IssuerAuthz::Capability) && access_rules->apply(oper, path);
604 if (scope_success && (m_log.getMsgMask() & LogMask::Debug)) {
605 std::stringstream ss;
606 ss << "Grant authorization based on scopes for operation=" << OpToName(oper) << ", path=" << path;
607 m_log.Log(LogMask::Debug, "Access", ss.str().c_str());
608 }
609
610 if (!scope_success && !mapping_success && !group_success) {
611 auto returned_accs = OnMissing(&new_secentity, path, oper, env);
612 // Clean up the new_secentity
613 if (new_secentity.vorg != nullptr) free(new_secentity.vorg);
614 if (new_secentity.grps != nullptr) free(new_secentity.grps);
615 if (new_secentity.role != nullptr) free(new_secentity.role);
616
617 return returned_accs;
618 }
619
620 // Default user only applies to scope-based mappings.
621 if (scope_success && username.empty()) {
622 username = access_rules->get_default_username();
623 }
624
625 // Setting the request.name will pass the username to the next plugin.
626 // Ensure we do that only if map-based or scope-based authorization worked.
627 if (scope_success || mapping_success) {
628 // Set scitokens.name in the extra attribute
629 Entity->eaAPI->Add("request.name", username, true);
630 new_secentity.eaAPI->Add("request.name", username, true);
631 m_log.Log(LogMask::Debug, "Access", "Request username", username.c_str());
632 }
633
634 // Make the token subject available. Even though it's a reasonably bad idea
635 // to use for *authorization* for file access, there may be other use cases.
636 // For example, the combination of (vorg, token.subject) is a reasonable
637 // approximation of a unique 'entity' (either person or a robot) and is
638 // more reasonable to use for resource fairshare in XrdThrottle.
639 const auto &token_subject = access_rules->get_token_subject();
640 if (!token_subject.empty()) {
641 Entity->eaAPI->Add("token.subject", token_subject, true);
642 }
643
644 // When the scope authorized this access, allow immediately. Otherwise, chain
645 XrdAccPrivs returned_op = scope_success ? AddPriv(oper, XrdAccPriv_None) : OnMissing(&new_secentity, path, oper, env);
646
647 // Since we are doing an early return, insert token info into the
648 // monitoring stream if monitoring is in effect and access granted
649 //
650 if (Entity->secMon && scope_success && returned_op && Mon_isIO(oper))
651 Mon_Report(new_secentity, token_subject, username);
652
653 // Cleanup the new_secentry
654 if (new_secentity.vorg != nullptr) free(new_secentity.vorg);
655 if (new_secentity.grps != nullptr) free(new_secentity.grps);
656 if (new_secentity.role != nullptr) free(new_secentity.role);
657
658 return returned_op;
659 }
XrdAccPrivs
@ XrdAccPriv_None
if(ec< 0) ec
bool Mon_isIO(const Access_Operation oper)
void Mon_Report(const XrdSecEntity &Entity, const std::string &subject, const std::string &username)
bool Add(XrdSecAttr &attr)
char * vorg
Entity's virtual organization(s).
int credslen
Length of the 'creds' data.
XrdNetAddrInfo * addrInfo
Entity's connection details.
XrdSecEntityAttr * eaAPI
non-const API to attributes
char prot[XrdSecPROTOIDSIZE]
Auth protocol used (e.g. krb5).
char * creds
Raw entity credentials or cert.
XrdSecMonitor * secMon
If !0 security monitoring enabled.
char * grps
Entity's group name(s).
char * role
Entity's role(s).

References XrdSecEntityAttr::Add(), XrdSecEntity::addrInfo, XrdSecEntity::creds, XrdSecEntity::credslen, XrdSecEntity::eaAPI, XrdOucEnv::Get(), XrdSecEntity::grps, XrdSciTokensMon::Mon_isIO(), XrdSciTokensMon::Mon_Report(), XrdSecEntity::prot, XrdSecEntity::role, XrdSecEntity::secMon, XrdSecEntity::vorg, and XrdAccPriv_None.

Here is the call graph for this function:

◆ Audit()

virtual int XrdAccSciTokens::Audit ( const int accok,
const XrdSecEntity * Entity,
const char * path,
const Access_Operation oper,
XrdOucEnv * Env = 0 )
inlineoverridevirtual

Route an audit message to the appropriate audit exit routine. See XrdAccAudit.h for more information on how the default implementation works. Currently, this method is not called by the ofs but should be used by the implementation to record denials or grants, as warranted.

Parameters
accok-> True is access was grated; false otherwise.
Entity-> Authentication information
path-> The logical path which is the target of oper
oper-> The operation being attempted (see above)
Env-> Environmental information at the time of the operation as supplied by the path CGI string. This is optional and the pointer may be zero.
Returns
Success: !0 information recorded. Failure: 0 information could not be recorded.

Implements XrdAccAuthorize.

Definition at line 728 of file XrdSciTokensAccess.cc.

733 {
734 return 0;
735 }

◆ GetConfigFile()

std::string XrdAccSciTokens::GetConfigFile ( )
inline

Definition at line 743 of file XrdSciTokensAccess.cc.

743 {
744 return m_cfg_file;
745 }

◆ IssuerList()

virtual Issuers XrdAccSciTokens::IssuerList ( )
inlineoverridevirtual

Implements XrdSciTokensHelper.

Definition at line 661 of file XrdSciTokensAccess.cc.

662 {
663 /*
664 Convert the m_issuers into the data structure:
665 struct ValidIssuer
666 {std::string issuer_name;
667 std::string issuer_url;
668 };
669 typedef std::vector<ValidIssuer> Issuers;
670 */
671 Issuers issuers;
672 for (auto it: m_issuers) {
673 ValidIssuer issuer_info;
674 issuer_info.issuer_name = it.first;
675 issuer_info.issuer_url = it.second.m_url;
676 issuers.push_back(issuer_info);
677 }
678 return issuers;
679
680 }
std::vector< ValidIssuer > Issuers

References XrdSciTokensHelper::ValidIssuer::issuer_name, and XrdSciTokensHelper::ValidIssuer::issuer_url.

◆ Test()

virtual int XrdAccSciTokens::Test ( const XrdAccPrivs priv,
const Access_Operation oper )
inlineoverridevirtual

Check whether the specified operation is permitted.

Parameters
priv-> the privileges as returned by Access().
oper-> The operation being attempted (see above)
Returns
Permit: a non-zero value (access is permitted) Deny: zero (access is denied)

Implements XrdAccAuthorize.

Definition at line 737 of file XrdSciTokensAccess.cc.

739 {
740 return (m_chain ? m_chain->Test(priv, oper) : 0);
741 }

◆ Validate()

virtual bool XrdAccSciTokens::Validate ( const char * token,
std::string & emsg,
long long * expT,
XrdSecEntity * entP )
inlineoverridevirtual

Validate a scitoken.

Parameters
token- Pointer to the token to validate.
emsg- Reference to a string to hold the reason for rejection
expT- Pointer to where the expiry value is to be placed. If nill, the value is not returned.
entP- Pointer to the SecEntity object and when not nil requests that it be filled with any identifying information in the token. The caller assumes that all supplied fields may be released by calling free().
Returns
Return true if the token is valid; false otherwise with emsg set.

Implements XrdSciTokensHelper.

Definition at line 682 of file XrdSciTokensAccess.cc.

684 {
685 // Just check if the token is valid, no scope checking
686
687 // Deserialize the token
688 SciToken scitoken;
689 char *err_msg;
690 if (!strncmp(token, "Bearer%20", 9)) token += 9;
691 pthread_rwlock_rdlock(&m_config_lock);
692 auto retval = scitoken_deserialize(token, &scitoken, &m_valid_issuers_array[0], &err_msg);
693 pthread_rwlock_unlock(&m_config_lock);
694 if (retval) {
695 // This originally looked like a JWT so log the failure.
696 m_log.Log(LogMask::Warning, "Validate", "Failed to deserialize SciToken:", err_msg);
697 emsg = err_msg;
698 free(err_msg);
699 return false;
700 }
701
702 // If an entity was passed then we will fill it in with the subject
703 // name, should it exist. Note that we are gauranteed that all the
704 // settable entity fields are null so no need to worry setting them.
705 //
706 if (Entity)
707 {char *value = nullptr;
708 if (!scitoken_get_claim_string(scitoken, "sub", &value, &err_msg))
709 Entity->name = strdup(value);
710 }
711
712 // Return the expiration time of this token if so wanted.
713 //
714 if (expT && scitoken_get_expiration(scitoken, expT, &err_msg)) {
715 emsg = err_msg;
716 free(err_msg);
717 return false;
718 }
719
720
721 // Delete the scitokens
722 scitoken_destroy(scitoken);
723
724 // Deserialize checks the key, so we're good now.
725 return true;
726 }
int emsg(int rc, char *msg)

References emsg(), and XrdSecEntity::name.

Here is the call graph for this function:

The documentation for this class was generated from the following file: