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

Constructor & Destructor Documentation

◆ XrdAccSciTokens()

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

Definition at line 478 of file XrdSciTokensAccess.cc.

478 :
479 m_chain(chain),
480 m_parms(parms ? parms : ""),
481 m_next_clean(monotonic_time() + m_expiry_secs),
482 m_log(lp, "scitokens_")
483 {
484 pthread_rwlock_init(&m_config_lock, nullptr);
485 m_config_lock_initialized = true;
486 m_log.Say("++++++ XrdAccSciTokens: Initialized SciTokens-based authorization.");
487 if (!Config(envP)) {
488 throw std::runtime_error("Failed to configure SciTokens authorization.");
489 }
490 }
void Say(const char *text1, const char *text2=0, const char *txt3=0, const char *text4=0, const char *text5=0, const char *txt6=0)

References XrdSysError::Say().

+ Here is the call graph for this function:

◆ ~XrdAccSciTokens()

virtual XrdAccSciTokens::~XrdAccSciTokens ( )
inlinevirtual

Definition at line 492 of file XrdSciTokensAccess.cc.

492 {
493 if (m_config_lock_initialized) {
494 pthread_rwlock_destroy(&m_config_lock);
495 }
496 }

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

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

References XrdSecEntityAttr::Add(), XrdSecEntity::addrInfo, XrdSecEntity::creds, XrdSecEntity::credslen, XrdSecEntity::eaAPI, XrdOucEnv::Get(), XrdSysError::getMsgMask(), XrdSecEntity::grps, XrdSysError::Log(), 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 730 of file XrdSciTokensAccess.cc.

735 {
736 return 0;
737 }

◆ GetConfigFile()

std::string XrdAccSciTokens::GetConfigFile ( )
inline

Definition at line 745 of file XrdSciTokensAccess.cc.

745 {
746 return m_cfg_file;
747 }

◆ IssuerList()

virtual Issuers XrdAccSciTokens::IssuerList ( )
inlineoverridevirtual

Implements XrdSciTokensHelper.

Definition at line 663 of file XrdSciTokensAccess.cc.

664 {
665 /*
666 Convert the m_issuers into the data structure:
667 struct ValidIssuer
668 {std::string issuer_name;
669 std::string issuer_url;
670 };
671 typedef std::vector<ValidIssuer> Issuers;
672 */
673 Issuers issuers;
674 for (auto it: m_issuers) {
675 ValidIssuer issuer_info;
676 issuer_info.issuer_name = it.first;
677 issuer_info.issuer_url = it.second.m_url;
678 issuers.push_back(issuer_info);
679 }
680 return issuers;
681
682 }
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 739 of file XrdSciTokensAccess.cc.

741 {
742 return (m_chain ? m_chain->Test(priv, oper) : 0);
743 }
virtual int Test(const XrdAccPrivs priv, const Access_Operation oper)=0

References XrdAccAuthorize::Test().

+ Here is the call graph for this function:

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

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

References emsg(), XrdSysError::Log(), and XrdSecEntity::name.

+ Here is the call graph for this function:

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