Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.sunbird.common;

public class Constants {
public static final String USER_ID = "userId";
public static final String SYSTEM = "system";
public static final String ACCESS_PUBLIC_KEY_PATH = "access.token.publickey.basepath";
public static final String READING_PUBLIC_KEY_EXCEPTION = "KeyManager:init: exception reading public key file:";
public static final String LOADING_PUBLIC_KEY_EXCEPTION = "KeyManager:init: exception loading public keys";
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.sunbird.common.Constants;
import org.sunbird.common.Platform;
import org.sunbird.telemetry.dto.Actor;
import org.sunbird.telemetry.dto.Context;
import org.sunbird.telemetry.dto.Producer;
import org.sunbird.telemetry.dto.Target;
import org.sunbird.telemetry.dto.Telemetry;
import org.sunbird.telemetry.logger.TelemetryRequestContext;

import java.util.ArrayList;
import java.util.HashMap;
Expand Down Expand Up @@ -77,6 +79,10 @@ public static String log(Map<String, String> context, String type, String level,
edata.put("pageid", pageid);
if (null != params && !params.isEmpty())
edata.put("params", getParamsList(params));
Object userIdFromContext = context.get(Constants.USER_ID);
if (userIdFromContext != null && StringUtils.isNotBlank(userIdFromContext.toString())) {
edata.put(Constants.USER_ID, userIdFromContext.toString());
}
Telemetry telemetry = new Telemetry("LOG", actor, eventContext, edata);
return getTelemetry(telemetry);
}
Expand Down Expand Up @@ -117,6 +123,10 @@ public static String error(Map<String, String> context, String code, String type
edata.put("pageid", pageid);
if (null != object)
edata.put("object", object);
String uid = context != null ? context.get(Constants.USER_ID) : null;
if (StringUtils.isNotBlank(uid)) {
edata.put(Constants.USER_ID, uid);
}
Telemetry telemetry = new Telemetry("ERROR", actor, eventContext, edata);
return getTelemetry(telemetry);

Expand Down Expand Up @@ -233,7 +243,9 @@ private static Context getContext(Map<String, String> context) {
String did = context.get("did");
if (StringUtils.isNotBlank(did))
eventContext.setDid(did);

if (StringUtils.isNotBlank(TelemetryRequestContext.getUserId())) {
context.put(Constants.USER_ID, TelemetryRequestContext.getUserId());
}
return eventContext;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.sunbird.common.Constants;
import org.sunbird.common.Platform;
import org.sunbird.common.exception.MiddlewareException;
import org.sunbird.common.exception.ResponseCode;
Expand Down Expand Up @@ -37,10 +38,12 @@ public class TelemetryManager {
* @param params
*/

public static void access(Map<String, String> context, Map<String, Object> params) {
String event = TelemetryGenerator.access(context, params);
telemetryHandler.send(event, Level.INFO, true);
}
public static void access(Map<String, String> context, Map<String, Object> params) {
Map<String, String> ctx = enrichContextWithUid(context);
Map<String, Object> enrichedParams = ensureParamsWithUid(params);
String event = TelemetryGenerator.access(ctx, enrichedParams);
telemetryHandler.send(event, Level.INFO, true);
}

/**
* To log only message as a telemetry event.
Expand Down Expand Up @@ -197,7 +200,14 @@ public static void search(Map<String, Object> context, String query, Object filt
}
}

String event = TelemetryGenerator.search(reqContext, query, filters, sort, null, size, topN, type);
String uid = TelemetryRequestContext.getUserId();
if (StringUtils.isNotBlank(uid)) {
if (reqContext == null) reqContext = new HashMap<>();
reqContext.put(TelemetryParams.ACTOR.name(), uid);
reqContext.put(Constants.USER_ID, uid);
}

String event = TelemetryGenerator.search(reqContext, query, filters, sort, null, size, topN, type);
telemetryHandler.send(event, Level.INFO, true);
}

Expand All @@ -210,12 +220,19 @@ public static void search(Map<String, Object> context, String query, Object filt
*/
private static void log(String message, Map<String, Object> params, String logLevel) {
Map<String, String> context = getContext();
String event = TelemetryGenerator.log(context, "system", logLevel, message, null, params);
Map<String, Object> enrichedParams = ensureParamsWithUid(params);
String event = TelemetryGenerator.log(context, Constants.SYSTEM, logLevel, message, null, enrichedParams);
telemetryHandler.send(event, Level.getLevel(logLevel));
}

private static Map<String, String> getContext() {
Map<String, String> context = new HashMap<String, String>();
String uid = TelemetryRequestContext.getUserId();

if (StringUtils.isNotBlank(uid)) {
context.put(TelemetryParams.ACTOR.name(), uid);
context.put(Constants.USER_ID, uid);
}
context.put(TelemetryParams.ACTOR.name(), "org.sunbird.learning.platform");
context.put(TelemetryParams.CHANNEL.name(), getContextValue("CHANNEL_ID", DEFAULT_CHANNEL_ID));
context.put(TelemetryParams.ENV.name(), getContextValue(TelemetryParams.ENV.name(), "system"));
Expand All @@ -232,4 +249,29 @@ public static void logRequestBody(String message) {
String event = TelemetryGenerator.log(context, "payload", Level.INFO.name(), message, null, null);
telemetryHandler.send(event, Level.INFO, true);
}

private static Map<String, Object> ensureParamsWithUid(Map<String, Object> params) {
String uid = TelemetryRequestContext.getUserId();
if (StringUtils.isNotBlank(uid)) {
if (params == null) params = new HashMap<>();
params.put(Constants.USER_ID, uid);
}
return params;
}

private static Map<String, String> enrichContextWithUid(Map<String, String> context) {
String uid = TelemetryRequestContext.getUserId();
Map<String, String> ctx = (context == null) ? new HashMap<>() : new HashMap<>(context);
if (StringUtils.isNotBlank(uid)) {
ctx.put(TelemetryParams.ACTOR.name(), uid);
ctx.put(Constants.USER_ID, uid);
}
if (!ctx.containsKey(TelemetryParams.CHANNEL.name())) {
ctx.put(TelemetryParams.CHANNEL.name(), DEFAULT_CHANNEL_ID);
}
if (!ctx.containsKey(TelemetryParams.ENV.name())) {
ctx.put(TelemetryParams.ENV.name(), Constants.SYSTEM);
}
return ctx;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Java
package org.sunbird.telemetry.logger;

import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import java.util.Map;

public final class TelemetryRequestContext {
private static final ThreadLocal<Map<String, String>> CTX = ThreadLocal.withInitial(HashMap::new);

private TelemetryRequestContext() { }

public static void setUserId(String userId) {
if (StringUtils.isNotBlank(userId)) CTX.get().put("userId", userId);
}

public static String getUserId() {
return CTX.get().get("userId");
}

public static void clear() {
CTX.remove();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.*;
import org.elasticsearch.index.query.MultiMatchQueryBuilder.Type;
Expand Down Expand Up @@ -548,29 +549,16 @@ private void formQueryImpl(List<Map> properties, QueryBuilder queryBuilder, Bool
}

private QueryBuilder checkNestedProperty(QueryBuilder queryBuilder, String propertyName) {
String cleanProp = propertyName.replaceAll(SearchConstants.RAW_FIELD_EXTENSION, "");
if (!cleanProp.contains(".")) {
if (StringUtils.isBlank(propertyName)) {
return queryBuilder;
}

String[] parts = cleanProp.split("\\.");
if (parts.length == 2) {
return QueryBuilders.nestedQuery(
parts[0],
queryBuilder,
org.apache.lucene.search.join.ScoreMode.None
).innerHit(new InnerHitBuilder());
String cleanProp = propertyName.replace(SearchConstants.RAW_FIELD_EXTENSION, "");
if (!cleanProp.contains(".")) {
return queryBuilder;
}
String nestedPath = cleanProp.substring(0, cleanProp.lastIndexOf('.'));

for (int i = parts.length - 2; i >= 0; i--) {
String path = String.join(".", Arrays.copyOfRange(parts, 0, i + 1));
queryBuilder = QueryBuilders.nestedQuery(
path,
queryBuilder,
org.apache.lucene.search.join.ScoreMode.None
).innerHit(new InnerHitBuilder());
}
return queryBuilder;
return QueryBuilders.nestedQuery(nestedPath, queryBuilder, ScoreMode.None);
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,4 +124,12 @@ public class SearchConstants {
public static final String OPERATION = "operation";
public static final String PROPERTY_NAME = "propertyName";
public static final String VALUES = "values";
public static final String SEARCH = "search";
public static final String X_AUTH_TOKEN = "x-authenticated-user-token";
public static final String CHANNEL_ID = "CHANNEL_ID";
public static final String CHANNEL_DEFAULT = "channel.default";
public static final String CONSUMER_ID = "CONSUMER_ID";
public static final String LEARNING_PLATFORM = "learning.platform";


}
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@ import akka.actor.ActorRef
import akka.pattern.Patterns
import org.apache.commons.lang3.StringUtils
import org.sunbird.common.dto.{RequestParams, Response, ResponseHandler}
import org.sunbird.common.exception.ResponseCode
import org.sunbird.common.exception.{ClientException, ResponseCode}
import org.sunbird.common.{DateUtils, JsonUtils, Platform}
import org.sunbird.search.util.SearchConstants
import org.sunbird.telemetry.TelemetryParams
import org.sunbird.telemetry.logger.TelemetryRequestContext
import play.api.mvc._

import java.util
import java.util.UUID
import scala.collection.JavaConversions._
import scala.concurrent.{ExecutionContext, Future}
import utils.AccessTokenValidator

abstract class SearchBaseController(protected val cc: ControllerComponents)(implicit exec: ExecutionContext) extends AbstractController(cc) {

Expand Down Expand Up @@ -110,17 +113,20 @@ abstract class SearchBaseController(protected val cc: ControllerComponents)(impl
request
}

protected def setHeaderContext(searchRequest: org.sunbird.common.dto.Request)(implicit playRequest: play.api.mvc.Request[AnyContent]) : Unit = {
protected def setHeaderContext(searchRequest: org.sunbird.common.dto.Request)(implicit playRequest: play.api.mvc.Request[AnyContent]): Unit = {
searchRequest.setContext(new util.HashMap[String, AnyRef]())
searchRequest.getContext.put(TelemetryParams.ENV.name, "search")
searchRequest.getContext.put(TelemetryParams.ENV.name, SearchConstants.SEARCH)
searchRequest.getContext.putAll(commonHeaders())
if (StringUtils.isBlank(searchRequest.getContext.getOrDefault("CHANNEL_ID", "").asInstanceOf[String])) {
searchRequest.getContext.put("CHANNEL_ID", Platform.config.getString("channel.default"))
val token = playRequest.headers.get(SearchConstants.X_AUTH_TOKEN).getOrElse("")
val userId = AccessTokenValidator.verifyUserToken(token, searchRequest.getContext)
TelemetryRequestContext.setUserId(userId)
if (StringUtils.isBlank(searchRequest.getContext.getOrDefault(SearchConstants.CHANNEL_ID, "").asInstanceOf[String])) {
searchRequest.getContext.put(SearchConstants.CHANNEL_ID, Platform.config.getString(SearchConstants.CHANNEL_DEFAULT))
}

if (null != searchRequest.getContext.get("CONSUMER_ID")) searchRequest.put(TelemetryParams.ACTOR.name, searchRequest.getContext.get("CONSUMER_ID"))
else if (null != searchRequest && null != searchRequest.getParams.getCid) searchRequest.put(TelemetryParams.ACTOR.name, searchRequest.getParams.getCid)
else searchRequest.put(TelemetryParams.ACTOR.name, "learning.platform")
if (null != searchRequest.getContext.get(SearchConstants.CONSUMER_ID) && (searchRequest.getContext.get(TelemetryParams.ACTOR.name) == null)) searchRequest.put(TelemetryParams.ACTOR.name, searchRequest.getContext.get("CONSUMER_ID"))
else if (null != searchRequest && null != searchRequest.getParams.getCid && (searchRequest.getContext.get(TelemetryParams.ACTOR.name) == null)) searchRequest.put(TelemetryParams.ACTOR.name, searchRequest.getParams.getCid)
else if (searchRequest.getContext.get(TelemetryParams.ACTOR.name) == null) searchRequest.put(TelemetryParams.ACTOR.name, SearchConstants.LEARNING_PLATFORM)
}

def getErrorResponse(apiId: String, version: String, errCode: String, errMessage: String): Future[Result] = {
Expand Down
85 changes: 85 additions & 0 deletions search-api/search-service/app/utils/AccessTokenValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package utils;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.keycloak.common.util.Time;
import org.sunbird.common.Platform;
import org.sunbird.telemetry.logger.TelemetryManager;

import java.io.IOException;
import java.util.Collections;
import java.util.Map;

public class AccessTokenValidator {
private AccessTokenValidator() { }
private static final ObjectMapper mapper = new ObjectMapper();
private static final String sso_url = Platform.config.getString("sso.url");
private static final String realm = Platform.config.getString("sso.realm");

private static Map<String, Object> validateToken(String token, Map<String, Object> requestContext)
throws IOException {
String[] tokenElements = token.split("\\.");
String header = tokenElements[0];
String body = tokenElements[1];
String signature = tokenElements[2];
String payLoad = header + "." + body;
Map<Object, Object> headerData =
mapper.readValue(new String(decodeFromBase64(header)), Map.class);
String keyId = headerData.get("kid").toString();
boolean isValid =
CryptoUtil.verifyRSASign(
payLoad,
decodeFromBase64(signature),
KeyManager.getPublicKey(keyId).getPublicKey(),
"SHA256withRSA",
requestContext);
if (isValid) {
Map<String, Object> tokenBody =
mapper.readValue(new String(decodeFromBase64(body)), Map.class);
boolean isExp = isExpired((Integer) tokenBody.get("exp"));
if (isExp) {
TelemetryManager.warn("Token is expired ");
return Collections.EMPTY_MAP;
}
return tokenBody;
}
return Collections.EMPTY_MAP;
}

public static String verifyUserToken(String token, Map<String, Object> requestContext) {
String userId = "UNAUTHORIZED";
try {
Map<String, Object> payload = validateToken(token, requestContext);
if (MapUtils.isNotEmpty(payload) && checkIss((String) payload.get("iss"))) {
userId = (String) payload.get("sub");
if (StringUtils.isNotBlank(userId)) {
int pos = userId.lastIndexOf(":");
userId = userId.substring(pos + 1);
}
}
} catch (Exception ex) {
TelemetryManager.error(
"Exception in verifyUserAccessToken: Token : ", ex);
}
if ("UNAUTHORIZED".equalsIgnoreCase(userId)) {
TelemetryManager.info(
"verifyUserAccessToken: Invalid User Token");
}
return userId;
}

private static boolean checkIss(String iss) {
String realmUrl = sso_url + "realms/" + realm;
return (realmUrl.equalsIgnoreCase(iss));
}


private static boolean isExpired(Integer expiration) {
return (Time.currentTime() > expiration);
}

private static byte[] decodeFromBase64(String data) {
return Base64Util.decode(data, 11);
}
}
Loading