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

Constructor & Destructor Documentation

◆ XrdAccSciTokens()

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

Definition at line 488 of file XrdSciTokensAccess.cc.

488 :
489 m_chain(chain),
490 m_parms(parms ? parms : ""),
491 m_next_clean(monotonic_time() + m_expiry_secs),
492 m_log(lp, "scitokens_")
493 {
494 pthread_rwlock_init(&m_config_lock, nullptr);
495 m_config_lock_initialized = true;
496 m_log.Say("++++++ XrdAccSciTokens: Initialized SciTokens-based authorization.");
497 if (!Config(envP)) {
498 throw std::runtime_error("Failed to configure SciTokens authorization.");
499 }
500 }

References XrdAccAuthorize::XrdAccAuthorize().

+ Here is the call graph for this function:

◆ ~XrdAccSciTokens()

virtual XrdAccSciTokens::~XrdAccSciTokens ( )
inlinevirtual

Definition at line 502 of file XrdSciTokensAccess.cc.

502 {
503 if (m_config_lock_initialized) {
504 pthread_rwlock_destroy(&m_config_lock);
505 }
506 }

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

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

754 {
755 return 0;
756 }

◆ GetConfigFile()

std::string XrdAccSciTokens::GetConfigFile ( )
inline

Definition at line 764 of file XrdSciTokensAccess.cc.

764 {
765 return m_cfg_file;
766 }

◆ IssuerList()

virtual Issuers XrdAccSciTokens::IssuerList ( )
inlineoverridevirtual

Implements XrdSciTokensHelper.

Definition at line 673 of file XrdSciTokensAccess.cc.

674 {
675 /*
676 Convert the m_issuers into the data structure:
677 struct ValidIssuer
678 {std::string issuer_name;
679 std::string issuer_url;
680 };
681 typedef std::vector<ValidIssuer> Issuers;
682 */
683 Issuers issuers;
684 pthread_rwlock_rdlock(&m_config_lock);
685 try {
686 for (const auto &it: m_issuers) {
687 issuers.push_back({it.first, it.second.m_url});
688 }
689 } catch (...) {
690 pthread_rwlock_unlock(&m_config_lock);
691 throw;
692 }
693 pthread_rwlock_unlock(&m_config_lock);
694 return issuers;
695
696 }
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 758 of file XrdSciTokensAccess.cc.

760 {
761 return (m_chain ? m_chain->Test(priv, oper) : 0);
762 }

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

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