XRootD
Loading...
Searching...
No Matches
Macaroons::Handler Class Reference

#include <XrdMacaroonsHandler.hh>

+ Inheritance diagram for Macaroons::Handler:
+ Collaboration diagram for Macaroons::Handler:

Public Types

enum  AuthzBehavior {
  PASSTHROUGH ,
  ALLOW ,
  DENY
}
 

Public Member Functions

 Handler (XrdSysError *log, const char *config, XrdOucEnv *myEnv, XrdAccAuthorize *chain)
 
virtual ~Handler ()
 
virtual int Init (const char *cfgfile) override
 Initializes the external request handler.
 
virtual bool MatchesPath (const char *verb, const char *path) override
 Tells if the incoming path is recognized as one of the paths that have to be processed.
 
virtual int ProcessReq (XrdHttpExtReq &req) override
 
- Public Member Functions inherited from XrdHttpExtHandler
 XrdHttpExtHandler ()
 Constructor.
 
virtual ~XrdHttpExtHandler ()
 Destructor.
 

Static Public Member Functions

static bool Config (const char *config, XrdOucEnv *env, XrdSysError *log, std::string &location, std::string &secret, ssize_t &max_duration, AuthzBehavior &behavior)
 

Detailed Description

Definition at line 38 of file XrdMacaroonsHandler.hh.

Member Enumeration Documentation

◆ AuthzBehavior

Enumerator
PASSTHROUGH 
ALLOW 
DENY 

Definition at line 53 of file XrdMacaroonsHandler.hh.

Constructor & Destructor Documentation

◆ Handler()

Macaroons::Handler::Handler ( XrdSysError * log,
const char * config,
XrdOucEnv * myEnv,
XrdAccAuthorize * chain )
inline

Definition at line 40 of file XrdMacaroonsHandler.hh.

41 :
42 m_max_duration(86400),
43 m_chain(chain),
44 m_log(log)
45 {
46 AuthzBehavior behavior;
47 if (!Config(config, myEnv, m_log, m_location, m_secret, m_max_duration, behavior))
48 {
49 throw std::runtime_error("Macaroon handler config failed.");
50 }
51 }
static bool Config(const char *config, XrdOucEnv *env, XrdSysError *log, std::string &location, std::string &secret, ssize_t &max_duration, AuthzBehavior &behavior)

References Config().

+ Here is the call graph for this function:

◆ ~Handler()

Handler::~Handler ( )
virtual

Definition at line 131 of file XrdMacaroonsHandler.cc.

132{
133 delete m_chain;
134}

Member Function Documentation

◆ Config()

bool Handler::Config ( const char * config,
XrdOucEnv * env,
XrdSysError * log,
std::string & location,
std::string & secret,
ssize_t & max_duration,
AuthzBehavior & behavior )
static

Definition at line 36 of file XrdMacaroonsConfigure.cc.

39{
40 XrdOucStream config_obj(log, getenv("XRDINSTANCE"), env, "=====> ");
41
42 // Open and attach the config file
43 //
44 int cfg_fd;
45 if ((cfg_fd = open(config, O_RDONLY, 0)) < 0) {
46 return log->Emsg("Config", errno, "open config file", config);
47 }
48 config_obj.Attach(cfg_fd);
49 static const char *cvec[] = { "*** macaroons plugin config:", 0 };
50 config_obj.Capture(cvec);
51
52 // Set default mask for logging.
54
55 // Set default maximum duration (24 hours).
56 max_duration = 24*3600;
57
58 // Process items
59 //
60 char *orig_var, *var;
61 bool success = true, ismine;
62 while ((orig_var = config_obj.GetMyFirstWord())) {
63 var = orig_var;
64 if ((ismine = !strncmp("all.sitename", var, 12))) var += 4;
65 else if ((ismine = !strncmp("macaroons.", var, 10)) && var[10]) var += 10;
66
67
68
69 if (!ismine) {continue;}
70
71 if (!strcmp("secretkey", var)) {success = xsecretkey(config_obj, log, secret);}
72 else if (!strcmp("sitename", var)) {success = xsitename(config_obj, log, location);}
73 else if (!strcmp("trace", var)) {success = xtrace(config_obj, log);}
74 else if (!strcmp("maxduration", var)) {success = xmaxduration(config_obj, log, max_duration);}
75 else if (!strcmp("onmissing", var)) {success = xonmissing(config_obj, log, behavior);}
76 else {
77 log->Say("Config warning: ignoring unknown directive '", orig_var, "'.");
78 config_obj.Echo();
79 continue;
80 }
81 if (!success) {
82 config_obj.Echo();
83 break;
84 }
85 }
86
87 if (success && !location.size())
88 {
89 log->Emsg("Config", "all.sitename must be specified to use macaroons.");
90 return false;
91 }
92
93 return success;
94}
static bool xonmissing(XrdOucStream &config_obj, XrdSysError *log, Handler::AuthzBehavior &behavior)
#define open
Definition XrdPosix.hh:76
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
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)
void setMsgMask(int mask)

References XrdOucStream::Attach(), XrdOucStream::Capture(), XrdOucStream::Echo(), XrdSysError::Emsg(), Macaroons::Error, XrdOucStream::GetMyFirstWord(), open, XrdSysError::Say(), XrdSysError::setMsgMask(), Macaroons::Warning, and xonmissing().

Referenced by Macaroons::Authz::Authz(), and Handler().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ Init()

virtual int Macaroons::Handler::Init ( const char * cfgfile)
inlineoverridevirtual

Initializes the external request handler.

Implements XrdHttpExtHandler.

Definition at line 64 of file XrdMacaroonsHandler.hh.

64{return 0;}

◆ MatchesPath()

bool Handler::MatchesPath ( const char * verb,
const char * path )
overridevirtual

Tells if the incoming path is recognized as one of the paths that have to be processed.

Implements XrdHttpExtHandler.

Definition at line 200 of file XrdMacaroonsHandler.cc.

201{
202 return !strcmp(verb, "POST") || !strncmp(path, "/.well-known/", 13) ||
203 !strncmp(path, "/.oauth2/", 9);
204}

◆ ProcessReq()

int Handler::ProcessReq ( XrdHttpExtReq & )
overridevirtual

Process an HTTP request and send the response using the calling XrdHttpProtocol instance directly Returns 0 if ok, non0 if errors

Implements XrdHttpExtHandler.

Definition at line 356 of file XrdMacaroonsHandler.cc.

357{
358 if (req.resource == "/.well-known/oauth-authorization-server") {
359 return ProcessOAuthConfig(req);
360 } else if (req.resource == "/.oauth2/token") {
361 return ProcessTokenRequest(req);
362 }
363
364 auto header = XrdOucTUtils::caseInsensitiveFind(req.headers,"content-type");
365 if (header == req.headers.end() || header->second != "application/macaroon-request")
366 return req.SendSimpleResp(415, nullptr, "accept: application/macaroon-request",
367 "Content-Type must be 'application/macaroon-request' to request a macaroon", false);
368
369 header = XrdOucTUtils::caseInsensitiveFind(req.headers,"content-length");
370 if (header == req.headers.end())
371 return req.SendSimpleResp(411, nullptr, nullptr, "Content-Length missing; not a valid POST", false);
372
373 ssize_t blen = std::strtoll(header->second.c_str(), nullptr, 10);
374
375 if (blen <= 0)
376 return req.SendSimpleResp(400, nullptr, nullptr, "Content-Length has invalid value.", false);
377
378 if (blen > 4096)
379 return req.SendSimpleResp(413, nullptr, nullptr, "Macaroon request too large (must be less than 4KB)", false);
380
381 // request_data is not necessarily null-terminated; hence, we use the more advanced _ex variant
382 // of the tokener to avoid making a copy of the character buffer.
383 char *request_data;
384 if (req.BuffgetData(blen, &request_data, true) != blen)
385 {
386 return req.SendSimpleResp(400, nullptr, nullptr, "Missing or invalid body of request.", 0);
387 }
388 json_tokener *tokener = json_tokener_new();
389 if (!tokener)
390 {
391 return req.SendSimpleResp(500, nullptr, nullptr, "Internal error when allocating token parser.", 0);
392 }
393 json_object *macaroon_req = json_tokener_parse_ex(tokener, request_data, blen);
394 enum json_tokener_error err = json_tokener_get_error(tokener);
395 json_tokener_free(tokener);
396 if (err != json_tokener_success)
397 {
398 if (macaroon_req) json_object_put(macaroon_req);
399 return req.SendSimpleResp(400, nullptr, nullptr, "Invalid JSON serialization of macaroon request.", 0);
400 }
401 json_object *validity_obj;
402 if (!json_object_object_get_ex(macaroon_req, "validity", &validity_obj))
403 {
404 json_object_put(macaroon_req);
405 return req.SendSimpleResp(400, nullptr, nullptr, "JSON request does not include a `validity`", 0);
406 }
407 const char *validity_cstr = json_object_get_string(validity_obj);
408 if (!validity_cstr)
409 {
410 json_object_put(macaroon_req);
411 return req.SendSimpleResp(400, nullptr, nullptr, "validity key cannot be cast to a string", 0);
412 }
413 std::string validity_str(validity_cstr);
414 ssize_t validity = determine_validity(validity_str);
415 if (validity <= 0)
416 {
417 json_object_put(macaroon_req);
418 return req.SendSimpleResp(400, nullptr, nullptr, "Invalid ISO 8601 duration for validity key", 0);
419 }
420 json_object *caveats_obj;
421 std::vector<std::string> other_caveats;
422 if (json_object_object_get_ex(macaroon_req, "caveats", &caveats_obj))
423 {
424 if (json_object_is_type(caveats_obj, json_type_array))
425 { // Caveats were provided. Let's record them.
426 // TODO - could just add these in-situ. No need for the other_caveats vector.
427 int array_length = json_object_array_length(caveats_obj);
428 other_caveats.reserve(array_length);
429 for (int idx=0; idx<array_length; idx++)
430 {
431 json_object *caveat_item = json_object_array_get_idx(caveats_obj, idx);
432 if (caveat_item)
433 {
434 const char *caveat_item_str = json_object_get_string(caveat_item);
435
436 if (!caveat_item_str) {
437 json_object_put(macaroon_req);
438 return req.SendSimpleResp(400, nullptr, nullptr, "Malformed or invalid caveat", 0);
439 }
440
441 if (is_reserved_caveat(caveat_item_str)) {
442 json_object_put(macaroon_req);
443 return req.SendSimpleResp(400, nullptr, nullptr,
444 "Cannot accept caveat with reserved key (name, path, before)\n", 0);
445 }
446
447 if (!is_supported_caveat(caveat_item_str)) {
448 json_object_put(macaroon_req);
449 return req.SendSimpleResp(400, nullptr, nullptr,
450 "Cannot accept caveat of unsupported type (supported types: activity)\n", 0);
451 }
452
453 other_caveats.emplace_back(caveat_item_str);
454 }
455 }
456 }
457 }
458 json_object_put(macaroon_req);
459
460 return GenerateMacaroonResponse(req, req.resource, other_caveats, validity, false);
461}
static bool is_supported_caveat(const std::string &cv)
static bool is_reserved_caveat(const std::string &cv)
static ssize_t determine_validity(const std::string &input)
static std::map< std::string, T >::const_iterator caseInsensitiveFind(const std::map< std::string, T > &m, const std::string &lowerCaseSearchKey)

References XrdHttpExtReq::BuffgetData(), XrdOucTUtils::caseInsensitiveFind(), determine_validity(), XrdHttpExtReq::headers, is_reserved_caveat(), is_supported_caveat(), XrdHttpExtReq::resource, and XrdHttpExtReq::SendSimpleResp().

+ Here is the call graph for this function:

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