use of org.folio.okapi.bean.ModuleInstance in project okapi by folio-org.
the class ProxyService method proxy.
public void proxy(RoutingContext ctx) {
ctx.request().pause();
ReadStream<Buffer> stream = ctx.request();
// Pause the request data stream before doing any slow ops, otherwise
// it will get read into a buffer somewhere.
ProxyContext pc = new ProxyContext(ctx);
// It would be nice to pass the request-id to the client, so it knows what
// to look for in Okapi logs. But that breaks the schemas, and RMB-based
// modules will not accept the response. Maybe later...
String tenantId = tenantHeader(pc);
if (tenantId == null) {
stream.resume();
// Error code already set in ctx
return;
}
sanitizeAuthHeaders(ctx.request().headers());
tenantManager.get(tenantId, gres -> {
if (gres.failed()) {
stream.resume();
pc.responseText(400, "No such Tenant " + tenantId);
return;
}
Tenant tenant = gres.result();
moduleManager.getEnabledModules(tenant, mres -> {
if (mres.failed()) {
stream.resume();
pc.responseError(mres.getType(), mres.cause());
return;
}
List<ModuleDescriptor> enabledModules = mres.result();
String metricKey = "proxy." + tenantId + "." + ctx.request().method() + "." + ctx.normalisedPath();
DropwizardHelper.markEvent(metricKey);
List<ModuleInstance> l = getModulesForRequest(pc, enabledModules);
if (l == null) {
stream.resume();
// ctx already set up
return;
}
pc.setModList(l);
pc.logRequest(ctx, tenantId);
ctx.request().headers().add(XOkapiHeaders.URL, okapiUrl);
ctx.request().headers().remove(XOkapiHeaders.MODULE_ID);
resolveUrls(l.iterator(), res -> {
if (res.failed()) {
stream.resume();
pc.responseError(res.getType(), res.cause());
} else {
proxyR(l.iterator(), pc, stream, null);
}
});
});
});
}
use of org.folio.okapi.bean.ModuleInstance in project okapi by folio-org.
the class TenantManager method ead1TenantInterface.
/**
* enableAndDisable helper 1: call the tenant interface.
*
* @param tenant
* @param mdFrom
* @param mdTo
* @param fut
*/
private void ead1TenantInterface(Tenant tenant, ModuleDescriptor mdFrom, ModuleDescriptor mdTo, ProxyContext pc, Handler<ExtendedAsyncResult<Void>> fut) {
if (mdTo == null) {
// disable only
ead5commit(tenant, mdFrom.getId(), null, pc, fut);
return;
}
getTenantInterface(mdTo, ires -> {
if (ires.failed()) {
if (ires.getType() == NOT_FOUND) {
logger.debug("eadTenantInterface: " + mdTo.getId() + " has no support for tenant init");
ead2PermMod(tenant, mdFrom, mdTo, pc, fut);
} else {
fut.handle(new Failure<>(ires.getType(), ires.cause()));
}
} else {
ModuleInstance tenInst = ires.result();
logger.debug("eadTenantInterface: tenint=" + tenInst.getPath());
JsonObject jo = new JsonObject();
jo.put("module_to", mdTo.getId());
if (mdFrom != null) {
jo.put("module_from", mdFrom.getId());
}
String req = jo.encodePrettily();
proxyService.callSystemInterface(tenant, tenInst, req, pc, cres -> {
if (cres.failed()) {
fut.handle(new Failure<>(cres.getType(), cres.cause()));
} else {
// We can ignore the result, the call went well.
ead2PermMod(tenant, mdFrom, mdTo, pc, fut);
}
});
}
});
}
use of org.folio.okapi.bean.ModuleInstance in project okapi by folio-org.
the class TenantManager method tenantPerms.
/**
* Helper to make the tenantPermissions call for one module. Used from
* ead3RealoadPerms and ead4Permissions.
*/
private void tenantPerms(Tenant tenant, ModuleDescriptor mdTo, ModuleDescriptor permsModule, ProxyContext pc, Handler<ExtendedAsyncResult<Void>> fut) {
pc.debug("Loading permissions for " + mdTo.getName() + " (using " + permsModule.getName() + ")");
String moduleTo = mdTo.getId();
PermissionList pl = new PermissionList(moduleTo, mdTo.getPermissionSets());
String pljson = Json.encodePrettily(pl);
pc.debug("tenantPerms Req: " + pljson);
InterfaceDescriptor permInt = permsModule.getSystemInterface("_tenantPermissions");
String permPath = "";
List<RoutingEntry> routingEntries = permInt.getAllRoutingEntries();
ModuleInstance permInst = null;
if (!routingEntries.isEmpty()) {
for (RoutingEntry re : routingEntries) {
if (re.match(null, "POST")) {
permPath = re.getPath();
if (permPath == null || permPath.isEmpty()) {
permPath = re.getPathPattern();
}
permInst = new ModuleInstance(permsModule, re, permPath);
}
}
}
if (permInst == null) {
fut.handle(new Failure<>(USER, "Bad _tenantPermissions interface in module " + permsModule.getId() + ". No path to POST to"));
return;
}
pc.debug("tenantPerms: " + permsModule.getId() + " and " + permPath);
proxyService.callSystemInterface(tenant, permInst, pljson, pc, cres -> {
if (cres.failed()) {
fut.handle(new Failure<>(cres.getType(), cres.cause()));
} else {
pc.debug("tenantPerms request to " + permsModule.getName() + " succeeded for module " + moduleTo + " and tenant " + tenant.getId());
fut.handle(new Success<>());
}
});
}
use of org.folio.okapi.bean.ModuleInstance in project okapi by folio-org.
the class TenantManager method getTenantInterface1.
private void getTenantInterface1(InterfaceDescriptor pi, ModuleDescriptor md, Handler<ExtendedAsyncResult<ModuleInstance>> fut) {
if (!"1.0".equals(pi.getVersion())) {
fut.handle(new Failure<>(USER, "Interface _tenant must be version 1.0 "));
return;
}
if ("system".equals(pi.getInterfaceType())) {
// looks like a new type
List<RoutingEntry> res = pi.getAllRoutingEntries();
if (!res.isEmpty()) {
for (RoutingEntry re : res) {
if (re.match(null, "POST")) {
if (re.getPath() != null) {
logger.debug("findTenantInterface: found path " + re.getPath());
fut.handle(new Success<>(new ModuleInstance(md, re, re.getPath())));
return;
}
if (re.getPathPattern() != null) {
logger.debug("findTenantInterface: found pattern " + re.getPathPattern());
fut.handle(new Success<>(new ModuleInstance(md, re, re.getPathPattern())));
return;
}
}
}
}
}
logger.warn("Module '" + md.getId() + "' uses old-fashioned tenant " + "interface. Define InterfaceType=system, and add a RoutingEntry." + " Falling back to calling /_/tenant.");
fut.handle(new Success<>(new ModuleInstance(md, null, "/_/tenant")));
}
use of org.folio.okapi.bean.ModuleInstance in project okapi by folio-org.
the class ProxyService method getModulesForRequest.
/**
* Builds the pipeline of modules to be invoked for a request.
* Sets the
* default authToken for each ModuleInstance. Later, these can be overwritten
* by the ModuleTokens from the auth, if needed.
*
* @param pc
* @param enabledModules modules enabled for the current tenant
* @return a list of ModuleInstances. In case of error, sets up ctx and
* returns null.
*/
private List<ModuleInstance> getModulesForRequest(ProxyContext pc, List<ModuleDescriptor> enabledModules) {
List<ModuleInstance> mods = new ArrayList<>();
HttpServerRequest req = pc.getCtx().request();
final String id = req.getHeader(XOkapiHeaders.MODULE_ID);
pc.debug("getMods: Matching " + req.method() + " " + req.absoluteURI());
for (ModuleDescriptor md : enabledModules) {
pc.debug("getMods: looking at " + md.getId());
List<RoutingEntry> rr = null;
if (id == null) {
rr = md.getProxyRoutingEntries();
} else if (id.equals(md.getId())) {
rr = md.getMultiRoutingEntries();
}
if (rr != null) {
for (RoutingEntry re : rr) {
if (match(re, req)) {
ModuleInstance mi = new ModuleInstance(md, re, req.uri());
mi.setAuthToken(pc.getCtx().request().headers().get(XOkapiHeaders.TOKEN));
mods.add(mi);
if (!resolveRedirects(pc, mods, re, enabledModules, "", req.uri(), "")) {
return null;
}
pc.debug("getMods: Added " + md.getId() + " " + re.getPathPattern() + " " + re.getPath() + " " + re.getPhase() + "/" + re.getLevel());
}
}
}
}
Comparator<ModuleInstance> cmp = (ModuleInstance a, ModuleInstance b) -> a.getRoutingEntry().getPhaseLevel().compareTo(b.getRoutingEntry().getPhaseLevel());
mods.sort(cmp);
// Check that our pipeline has a real module in it, not just filters,
// so that we can return a proper 404 for requests that only hit auth
pc.debug("Checking filters for " + req.absoluteURI());
boolean found = false;
for (ModuleInstance inst : mods) {
pc.debug("getMods: Checking " + inst.getRoutingEntry().getPathPattern() + " " + "'" + inst.getRoutingEntry().getPhase() + "' " + "'" + inst.getRoutingEntry().getLevel() + "' ");
if (inst.getRoutingEntry().getPhase() == null) {
// No real handler should have a phase any more.
found = true;
// It has been deprecated for a long time, and never made any sense anyway.
// The auth filter, the only one we have, uses phase 'auth'
}
}
if (!found) {
if (// If we defaulted to supertenant,
"-".equals(pc.getTenant()) && !req.path().startsWith("/_/")) {
// and not wrong okapi request
// The /_/ test is to make sure we report same errors as before internalModule stuff
pc.responseText(403, "Missing Tenant");
return null;
} else {
pc.responseError(404, "No suitable module found for path " + req.path());
return null;
}
}
return mods;
}
Aggregations