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.
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 470 of file XrdSciTokensAccess.cc.

Constructor & Destructor Documentation

◆ XrdAccSciTokens()

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

Definition at line 481 of file XrdSciTokensAccess.cc.

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

References XrdAccAuthorize::XrdAccAuthorize().

Here is the call graph for this function:

◆ ~XrdAccSciTokens()

virtual XrdAccSciTokens::~XrdAccSciTokens ( )
inlinevirtual

Definition at line 495 of file XrdSciTokensAccess.cc.

495 {
496 if (m_config_lock_initialized) {
497 pthread_rwlock_destroy(&m_config_lock);
498 }
499 }

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 501 of file XrdSciTokensAccess.cc.

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

747 {
748 return 0;
749 }

◆ GetConfigFile()

std::string XrdAccSciTokens::GetConfigFile ( )
inline

Definition at line 757 of file XrdSciTokensAccess.cc.

757 {
758 return m_cfg_file;
759 }

◆ IssuerList()

virtual Issuers XrdAccSciTokens::IssuerList ( )
inlineoverridevirtual

Implements XrdSciTokensHelper.

Definition at line 666 of file XrdSciTokensAccess.cc.

667 {
668 /*
669 Convert the m_issuers into the data structure:
670 struct ValidIssuer
671 {std::string issuer_name;
672 std::string issuer_url;
673 };
674 typedef std::vector<ValidIssuer> Issuers;
675 */
676 Issuers issuers;
677 pthread_rwlock_rdlock(&m_config_lock);
678 try {
679 for (const auto &it: m_issuers) {
680 issuers.push_back({it.first, it.second.m_url});
681 }
682 } catch (...) {
683 pthread_rwlock_unlock(&m_config_lock);
684 throw;
685 }
686 pthread_rwlock_unlock(&m_config_lock);
687 return issuers;
688
689 }
std::vector< ValidIssuer > Issuers

◆ 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 751 of file XrdSciTokensAccess.cc.

753 {
754 return (m_chain ? m_chain->Test(priv, oper) : 0);
755 }

◆ 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 691 of file XrdSciTokensAccess.cc.

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