Skip to content

Commit

Permalink
Merge pull request #1763 from akto-api-security/hotfix/cache_feature_…
Browse files Browse the repository at this point in the history
…label

Addinf cache map and logs
  • Loading branch information
avneesh-akto authored Nov 22, 2024
2 parents b77bdf2 + 208ab74 commit 0537b4f
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,9 @@
import com.akto.audit_logs_util.AuditLogsUtil;
import com.akto.dao.RBACDao;
import com.akto.dao.audit_logs.ApiAuditLogsDao;
import com.akto.dao.billing.OrganizationsDao;
import com.akto.dao.context.Context;
import com.akto.dto.User;
import com.akto.dto.audit_logs.ApiAuditLogs;
import com.akto.dto.billing.FeatureAccess;
import com.akto.dto.billing.Organization;
import com.akto.dto.RBAC.Role;
import com.akto.dto.rbac.RbacEnums;
import com.akto.dto.rbac.RbacEnums.Feature;
Expand All @@ -17,23 +14,24 @@
import com.akto.log.LoggerMaker;
import com.akto.log.LoggerMaker.LogDb;
import com.akto.runtime.policies.UserAgentTypePolicy;
import com.akto.usage.UsageMetricCalculator;
import com.akto.util.DashboardMode;
import com.mongodb.client.model.Filters;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import org.apache.struts2.ServletActionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class RoleAccessInterceptor extends AbstractInterceptor {

private static final LoggerMaker loggerMaker = new LoggerMaker(RoleAccessInterceptor.class, LoggerMaker.LogDb.DASHBOARD);

private static final Logger logger = LoggerFactory.getLogger(RoleAccessInterceptor.class);
String featureLabel;
String accessType;
String actionDescription;
Expand All @@ -52,18 +50,6 @@ public void setActionDescription(String actionDescription) {

public final static String FORBIDDEN = "FORBIDDEN";
private final static String USER = "user";
private final static String FEATURE_LABEL_STRING = "RBAC_FEATURE";

private boolean checkForPaidFeature(int accountId){
Organization organization = OrganizationsDao.instance.findOne(Filters.in(Organization.ACCOUNTS, accountId));
if(organization == null || organization.getFeatureWiseAllowed() == null || organization.getFeatureWiseAllowed().isEmpty()){
return true;
}

HashMap<String, FeatureAccess> featureWiseAllowed = organization.getFeatureWiseAllowed();
FeatureAccess featureAccess = featureWiseAllowed.getOrDefault(FEATURE_LABEL_STRING, FeatureAccess.noAccess);
return featureAccess.getIsGranted();
}

private int getUserAccountId (Map<String, Object> session) throws Exception{
try {
Expand All @@ -82,9 +68,9 @@ private int getUserAccountId (Map<String, Object> session) throws Exception{
@Override
public String intercept(ActionInvocation invocation) throws Exception {
ApiAuditLogs apiAuditLogs = null;
int timeNow = Context.now();
try {
HttpServletRequest request = ServletActionContext.getRequest();

if(featureLabel == null) {
throw new Exception("Feature list is null or empty");
}
Expand All @@ -101,24 +87,33 @@ public String intercept(ActionInvocation invocation) throws Exception {
}
int sessionAccId = getUserAccountId(session);

logger.info("Found sessionId in : " + (Context.now() - timeNow));
timeNow = Context.now();


if(!DashboardMode.isMetered()){
return invocation.invoke();
}

if(!(checkForPaidFeature(sessionAccId) || featureLabel.equalsIgnoreCase(RbacEnums.Feature.ADMIN_ACTIONS.toString()))){
if(!(UsageMetricCalculator.isRbacFeatureAvailable(sessionAccId) || featureLabel.equalsIgnoreCase(RbacEnums.Feature.ADMIN_ACTIONS.toString()))){
logger.info("Time by feature label check in: " + (Context.now() - timeNow));
return invocation.invoke();
}

loggerMaker.infoAndAddToDb("Found user in interceptor: " + user.getLogin(), LogDb.DASHBOARD);
logger.info("Time by feature label check in: " + (Context.now() - timeNow));
timeNow = Context.now();

loggerMaker.infoAndAddToDb("Found user in interceptor: " + user.getLogin(), LogDb.DASHBOARD);
int userId = user.getId();

Role userRoleRecord = RBACDao.getCurrentRoleForUser(userId, sessionAccId);
logger.info("Found user role in: " + (Context.now() - timeNow));
String userRole = userRoleRecord != null ? userRoleRecord.getName().toUpperCase() : "";

if(userRole == null || userRole.isEmpty()) {
throw new Exception("User role not found");
}

Feature featureType = Feature.valueOf(this.featureLabel.toUpperCase());

ReadWriteAccess accessGiven = userRoleRecord.getReadWriteAccessForFeature(featureType);
Expand Down
30 changes: 30 additions & 0 deletions libs/utils/src/main/java/com/akto/usage/UsageMetricCalculator.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.akto.usage;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import com.akto.dao.ApiCollectionsDao;
Expand All @@ -11,6 +12,7 @@
import com.akto.dao.testing.TestingRunResultDao;
import com.akto.dto.ApiCollection;
import com.akto.dto.ApiInfo;
import com.akto.dto.billing.FeatureAccess;
import com.akto.dto.billing.Organization;
import com.akto.dto.test_editor.YamlTemplate;
import com.akto.dto.testing.TestResult;
Expand All @@ -20,6 +22,7 @@
import com.akto.dto.usage.UsageMetric;
import com.akto.dto.usage.metadata.ActiveAccounts;
import com.akto.log.LoggerMaker;
import com.akto.util.Pair;
import com.akto.util.enums.GlobalEnums.YamlTemplateSource;
import com.google.gson.Gson;
import com.mongodb.client.model.Filters;
Expand All @@ -43,10 +46,14 @@ public static Set<Integer> getDemos() {
/*
* to handle multiple accounts using static maps.
*/
private final static String FEATURE_LABEL_STRING = "RBAC_FEATURE";
private static Map<Integer, Integer> lastDeactivatedFetchedMap = new HashMap<>();
private static final int REFRESH_INTERVAL = 60 * 2; // 2 minutes.
private static final int REFRESH_INTERVAL_RBAC = 60 * 60; // 1 hour.
private static Map<Integer, Set<Integer>> deactivatedCollectionsMap = new HashMap<>();

private static final ConcurrentHashMap<Integer, Pair<Boolean, Integer>> hasRbacFeatureEnabledMap = new ConcurrentHashMap<>();

public static Set<Integer> getDeactivated() {
int accountId = Context.accountId.get();
if (lastDeactivatedFetchedMap.containsKey(accountId)
Expand All @@ -60,6 +67,29 @@ public static Set<Integer> getDeactivated() {
return deactivatedCollectionsMap.get(accountId);
}

private static boolean checkForPaidFeature(int accountId){
Organization organization = OrganizationsDao.instance.findOne(Filters.in(Organization.ACCOUNTS, accountId));
if(organization == null || organization.getFeatureWiseAllowed() == null || organization.getFeatureWiseAllowed().isEmpty()){
return true;
}

HashMap<String, FeatureAccess> featureWiseAllowed = organization.getFeatureWiseAllowed();
FeatureAccess featureAccess = featureWiseAllowed.getOrDefault(FEATURE_LABEL_STRING, FeatureAccess.noAccess);
return featureAccess.getIsGranted();
}

public static boolean isRbacFeatureAvailable(int accountId){
int timeNow = Context.now();
Pair<Boolean, Integer> prevVal = hasRbacFeatureEnabledMap.getOrDefault(accountId, new Pair<>(false, timeNow));
boolean ans = prevVal.getFirst();
int lastCalTime = prevVal.getSecond();
if(!hasRbacFeatureEnabledMap.contains(accountId) || (lastCalTime + REFRESH_INTERVAL_RBAC < timeNow)){
ans = checkForPaidFeature(accountId);
hasRbacFeatureEnabledMap.put(accountId, new Pair<>(ans, timeNow));
}
return ans;
}

public static Set<Integer> getDeactivatedLatest(){
List<ApiCollection> deactivated = ApiCollectionsDao.instance
.findAll(Filters.eq(ApiCollection._DEACTIVATED, true));
Expand Down

0 comments on commit 0537b4f

Please sign in to comment.