Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement ability to have the token username as a separate claim #1978

Merged
merged 1 commit into from
Apr 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/XrdSciTokens/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ are:
- `default_user` (optional): If set, then all authorized operations will be done under the provided username when
interacting with the filesystem. This is useful in the case where the administrator desires that all files owned
by an issuer should be mapped to a particular Unix user account at the site.
- `username_claim` (optional): Not all issuers put the desired username in the `sub` claim (sometimes the subject is
set to a de-identified value). To use an alternate claim as the username, such as `uid`, set this to the desired
claim name. If set, it overrides `map_subject` and `default_user`.
- `name_mapfile` (options): If set, then the referenced file is parsed as a JSON object and the specified mappings
are applied to the username inside the XRootD framework. See below for more information on the mapfile.

Expand All @@ -124,19 +127,24 @@ Mapfile format
The file specified by the `name_mapfile` attribute can be used to perform identity mapping for a given issuer.
It must parse as valid JSON and may look like this:

```
[
{"sub": "bbockelm", "path": "/home/bbockelm", "result": "bbockelm"},
{"group": "/cms/prod", "path": "/cms", "result": "cmsprod" comment="Added 1 Sept 2020"},
{"group": "/cms", "result": "cmsuser"},
{"group": "/cms", "result": "atlas" ignore="Only for testing"}
]
```

That is, we have a JSON list of objects; each object is interpreted as a rule. For an incoming request to match a rule,
each present attribute must evaluate to true. In this case, the value of the `result` key is populated as the username
in the XRootD internal credential.

The enumerated keys are:
- `sub`: True if the `sub` claim in the token matches the value in the mapfile (case-sensitive comparison).
- `username`: True if the username in the token (the claim specifying the username is configurable, controlled by the
`username_claim` variable in the issuer config; default is `sub`) matches the value in the mapfile (case-sensitive
comparison).
- `path`: True iff the value of the attribute matches (case-sensitive) the prefix of the (normalized) requested path.
For example, if the issuer's base path is `/home`, the operation is accessing `/home/bbockelm/foo`, and the path in
the rule is `/bbockelm`, then this attribute evaluates to `true`. Note the path value and the requested path must
Expand Down
60 changes: 40 additions & 20 deletions src/XrdSciTokens/XrdSciTokensAccess.cc
Original file line number Diff line number Diff line change
Expand Up @@ -218,23 +218,28 @@ void ParseCanonicalPaths(const std::string &path, std::vector<std::string> &resu
struct MapRule
{
MapRule(const std::string &sub,
const std::string &username,
const std::string &path_prefix,
const std::string &group,
const std::string &name)
const std::string &result)
: m_sub(sub),
m_username(username),
m_path_prefix(path_prefix),
m_group(group),
m_name(name)
m_result(result)
{
//std::cerr << "Making a rule {sub=" << sub << ", path=" << path_prefix << ", group=" << group << ", result=" << name << "}" << std::endl;
//std::cerr << "Making a rule {sub=" << sub << ", username=" << username << ", path=" << path_prefix << ", group=" << group << ", result=" << name << "}" << std::endl;
}

const std::string match(const std::string sub,
const std::string req_path,
const std::vector<std::string> groups) const
const std::string match(const std::string &sub,
const std::string &username,
const std::string &req_path,
const std::vector<std::string> &groups) const
{
if (!m_sub.empty() && sub != m_sub) {return "";}

if (!m_username.empty() && username != username) {return "";}

if (!m_path_prefix.empty() &&
strncmp(req_path.c_str(), m_path_prefix.c_str(), m_path_prefix.size()))
{
Expand All @@ -244,17 +249,18 @@ struct MapRule
if (!m_group.empty()) {
for (const auto &group : groups) {
if (group == m_group)
return m_name;
return m_result;
}
return "";
}
return m_name;
return m_result;
}

std::string m_sub;
std::string m_username;
std::string m_path_prefix;
std::string m_group;
std::string m_name;
std::string m_result;
};

struct IssuerConfig
Expand All @@ -265,11 +271,13 @@ struct IssuerConfig
const std::vector<std::string> &restricted_paths,
bool map_subject,
const std::string &default_user,
const std::string &username_claim,
const std::vector<MapRule> rules)
: m_map_subject(map_subject),
: m_map_subject(map_subject || !username_claim.empty()),
m_name(issuer_name),
m_url(issuer_url),
m_default_user(default_user),
m_username_claim(username_claim),
m_base_paths(base_paths),
m_restricted_paths(restricted_paths),
m_map_rules(rules)
Expand All @@ -279,6 +287,7 @@ struct IssuerConfig
const std::string m_name;
const std::string m_url;
const std::string m_default_user;
const std::string m_username_claim;
const std::vector<std::string> m_base_paths;
const std::vector<std::string> m_restricted_paths;
const std::vector<MapRule> m_map_rules;
Expand Down Expand Up @@ -358,7 +367,7 @@ class XrdAccRules
std::string get_username(const std::string &req_path) const
{
for (const auto &rule : m_map_rules) {
std::string name = rule.match(m_token_subject, req_path, m_groups);
std::string name = rule.match(m_token_subject, m_username, req_path, m_groups);
if (!name.empty()) {
return name;
}
Expand Down Expand Up @@ -614,7 +623,7 @@ class XrdAccSciTokens : public XrdAccAuthorize, public XrdSciTokensHelper
}

virtual bool Validate(const char *token, std::string &emsg, long long *expT,
XrdSecEntity *Entity)
XrdSecEntity *Entity) override
{
// Just check if the token is valid, no scope checking

Expand Down Expand Up @@ -811,10 +820,18 @@ class XrdAccSciTokens : public XrdAccAuthorize, public XrdSciTokensHelper
token_subject = std::string(value);
free(value);

std::string tmp_username;
if (config.m_map_subject) {
tmp_username = token_subject;
} else {
auto tmp_username = token_subject;
if (!config.m_username_claim.empty()) {
if (scitoken_get_claim_string(token, config.m_username_claim.c_str(), &value, &err_msg)) {
pthread_rwlock_unlock(&m_config_lock);
m_log.Log(LogMask::Warning, "GenerateAcls", "Failed to get token username:", err_msg);
free(err_msg);
scitoken_destroy(token);
return false;
}
tmp_username = std::string(value);
free(value);
} else if (!config.m_map_subject) {
tmp_username = config.m_default_user;
}

Expand Down Expand Up @@ -972,6 +989,7 @@ class XrdAccSciTokens : public XrdAccAuthorize, public XrdSciTokensHelper
std::string path;
std::string group;
std::string sub;
std::string username;
std::string result;
bool ignore = false;
for (const auto &entry : rule.get<picojson::object>()) {
Expand All @@ -989,8 +1007,9 @@ class XrdAccSciTokens : public XrdAccAuthorize, public XrdSciTokensHelper
}
else if (entry.first == "sub") {
sub = entry.second.get<std::string>();
}
else if (entry.first == "path") {
} else if (entry.first == "username") {
username = entry.second.get<std::string>();
} else if (entry.first == "path") {
std::string norm_path;
if (!MakeCanonical(entry.second.get<std::string>(), norm_path)) {
ss << "In mapfile " << filename << " encountered a path " << entry.second.get<std::string>()
Expand All @@ -1011,7 +1030,7 @@ class XrdAccSciTokens : public XrdAccAuthorize, public XrdSciTokensHelper
m_log.Log(LogMask::Error, "ParseMapfile", ss.str().c_str());
return false;
}
rules.emplace_back(sub, path, group, result);
rules.emplace_back(sub, username, path, group, result);
}

return true;
Expand Down Expand Up @@ -1158,11 +1177,12 @@ class XrdAccSciTokens : public XrdAccAuthorize, public XrdSciTokensHelper

auto default_user = reader.Get(section, "default_user", "");
auto map_subject = reader.GetBoolean(section, "map_subject", false);
auto username_claim = reader.Get(section, "username_claim", "");

issuers.emplace(std::piecewise_construct,
std::forward_as_tuple(issuer),
std::forward_as_tuple(name, issuer, base_paths, restricted_paths,
map_subject, default_user, rules));
map_subject, default_user, username_claim, rules));
}

if (issuers.empty()) {
Expand Down