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

Permissions cache #1640

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
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
5 changes: 5 additions & 0 deletions Core/classes/env.properties
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,12 @@ db.nosql.runCorruptionOnStartupIfDirty=false
#Size of in memory cache to hold a role's inheritance list, this represents the
# maximum number of roles to keep in the cache at any given time
cache.roles.size=1000
#Size of in memory cache for Users, this represents the
# maximum number of roles to keep in the cache at any given time
cache.users.size=1000
#Size of in memory cache to hold created Permissions, this represents the
# maximum number of roles to keep in the cache at any given time
cache.permission.size=1000

# The location of the Mango Automation store from which to get license files.
store.url=https://store.infiniteautomation.com
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,34 @@
*/
package com.infiniteautomation.mango.spring.service;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.event.EventListener;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.stereotype.Service;

import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.infiniteautomation.mango.permission.MangoPermission;
import com.infiniteautomation.mango.spring.MangoRuntimeContextConfiguration;
import com.infiniteautomation.mango.spring.events.DaoEvent;
import com.infiniteautomation.mango.util.Functions;
import com.infiniteautomation.mango.util.exception.NotFoundException;
import com.serotonin.m2m2.Common;
import com.serotonin.m2m2.db.dao.PermissionDao;
import com.serotonin.m2m2.db.dao.RoleDao;
import com.serotonin.m2m2.i18n.ProcessResult;
import com.serotonin.m2m2.i18n.TranslatableMessage;
Expand All @@ -24,24 +45,6 @@
import com.serotonin.m2m2.vo.permission.PermissionHolder;
import com.serotonin.m2m2.vo.role.Role;
import com.serotonin.m2m2.vo.role.RoleVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.event.EventListener;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.stereotype.Service;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
* @author Terry Packer
Expand All @@ -51,26 +54,34 @@
public class PermissionService {

private final RoleDao roleDao;
private final PermissionDao permissionDao;
private final DataSourcePermissionDefinition dataSourcePermission;
private final PermissionHolder systemSuperadmin;
private final EventsViewPermissionDefinition eventsViewPermission;

//Cache of role xid to inheritance
private final LoadingCache<String, RoleInheritance> roleHierarchyCache;
//Cache of permissionId to MangoPermission
private final LoadingCache<Integer, MangoPermission> permissionCache;

@Autowired
public PermissionService(RoleDao roleDao,
PermissionDao permissionDao,
@Qualifier(MangoRuntimeContextConfiguration.SYSTEM_SUPERADMIN_PERMISSION_HOLDER)
PermissionHolder systemSuperadmin,
DataSourcePermissionDefinition dataSourcePermission,
EventsViewPermissionDefinition eventsView) {
this.roleDao = roleDao;
this.permissionDao = permissionDao;
this.dataSourcePermission = dataSourcePermission;
this.systemSuperadmin = systemSuperadmin;
this.eventsViewPermission = eventsView;
this.roleHierarchyCache = Caffeine.newBuilder()
.maximumSize(Common.envProps.getLong("cache.roles.size", 1000))
.build(this::loadRoleInheritance);
this.permissionCache = Caffeine.newBuilder()
.maximumSize(Common.envProps.getLong("cache.permission.size", 1000))
.build(this::loadPermission);
}

/**
Expand Down Expand Up @@ -445,6 +456,55 @@ public Set<Role> getAllInheritedRoles(PermissionHolder holder) {
return Collections.unmodifiableSet(allRoles);
}

/**
* Get a permission from the cache, load from db if necessary
* @param id
* @return
* @throws NotFoundException if permission with this ID not found
*/
public MangoPermission get(Integer id) throws NotFoundException {
MangoPermission permission = this.permissionCache.get(id);
if(permission == null) {
throw new NotFoundException();
}else {
return permission;
}
}

/**
* Get/Create a permission based on the minterms of this permission
* and return the id for it. This is done before saving a VO with a
* permission so there is a FK to reference.
* @param permission
* @return
*/
public Integer permissionId(MangoPermission permission) {
//TODO Mango 4.0 use cache, need to be able to quickly find a permission
// without using the ID.
return permissionDao.permissionId(permission);
}

/**
* A vo with a permission was deleted, attempt to delete it and clean up
* if other VOs reference this permission it will not be deleted
* @param permission
*/
public void permissionDeleted(MangoPermission permission) {
if(permissionDao.permissionDeleted(permission.getId())) {
this.permissionCache.invalidate(permission.getId());
}
}

/**
* Load a permission from the database via it's id, used by cache
* @param id
* @return
*/
private MangoPermission loadPermission(Integer id) {
//TODO Mango 4.0 throw NotFoundException?
return permissionDao.get(id);
}

/**
* Validate a permission. This will validate that:
*
Expand Down
105 changes: 64 additions & 41 deletions Core/src/com/serotonin/m2m2/db/dao/DataPointDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,55 @@
*/
package com.serotonin.m2m2.db.dao;

import static com.serotonin.m2m2.db.dao.DataPointTagsDao.DATA_POINT_TAGS_PIVOT_ALIAS;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jooq.Condition;
import org.jooq.Field;
import org.jooq.Name;
import org.jooq.Record;
import org.jooq.Select;
import org.jooq.SelectJoinStep;
import org.jooq.SortField;
import org.jooq.Table;
import org.jooq.impl.DSL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.infiniteautomation.mango.db.query.*;
import com.infiniteautomation.mango.db.query.ConditionSortLimit;
import com.infiniteautomation.mango.db.query.ConditionSortLimitWithTagKeys;
import com.infiniteautomation.mango.db.query.RQLSubSelectCondition;
import com.infiniteautomation.mango.db.query.RQLToCondition;
import com.infiniteautomation.mango.db.query.RQLToConditionWithTagKeys;
import com.infiniteautomation.mango.permission.MangoPermission;
import com.infiniteautomation.mango.spring.MangoRuntimeContextConfiguration;
import com.infiniteautomation.mango.spring.db.*;
import com.infiniteautomation.mango.spring.db.DataPointTableDefinition;
import com.infiniteautomation.mango.spring.db.DataSourceTableDefinition;
import com.infiniteautomation.mango.spring.db.EventDetectorTableDefinition;
import com.infiniteautomation.mango.spring.db.EventHandlerTableDefinition;
import com.infiniteautomation.mango.spring.db.RoleTableDefinition;
import com.infiniteautomation.mango.spring.db.UserCommentTableDefinition;
import com.infiniteautomation.mango.spring.events.DaoEvent;
import com.infiniteautomation.mango.spring.events.DaoEventType;
import com.infiniteautomation.mango.spring.events.DataPointTagsUpdatedEvent;
Expand Down Expand Up @@ -38,28 +82,6 @@
import com.serotonin.m2m2.vo.permission.PermissionHolder;
import com.serotonin.provider.Providers;
import com.serotonin.util.SerializationHelper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jooq.*;
import org.jooq.impl.DSL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Comparator;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.serotonin.m2m2.db.dao.DataPointTagsDao.DATA_POINT_TAGS_PIVOT_ALIAS;

/**
*
Expand All @@ -74,7 +96,6 @@ public class DataPointDao extends AbstractVoDao<DataPointVO, DataPointTableDefin
private final EventDetectorTableDefinition eventDetectorTable;
private final UserCommentTableDefinition userCommentTable;
private final PermissionService permissionService;
private final PermissionDao permissionDao;
private final DataPointTagsDao dataPointTagsDao;
private final EventDetectorDao eventDetectorDao;

Expand All @@ -90,7 +111,6 @@ private DataPointDao(DataPointTableDefinition table,
PermissionService permissionService,
@Qualifier(MangoRuntimeContextConfiguration.DAO_OBJECT_MAPPER_NAME)ObjectMapper mapper,
ApplicationEventPublisher publisher,
PermissionDao permissionDao,
DataPointTagsDao dataPointTagsDao,
EventDetectorDao eventDetectorDao) {
super(EventType.EventTypeNames.DATA_POINT, table,
Expand All @@ -100,7 +120,6 @@ private DataPointDao(DataPointTableDefinition table,
this.eventDetectorTable = eventDetectorTable;
this.userCommentTable = userCommentTable;
this.permissionService = permissionService;
this.permissionDao = permissionDao;
this.dataPointTagsDao = dataPointTagsDao;
this.eventDetectorDao = eventDetectorDao;
}
Expand Down Expand Up @@ -303,7 +322,9 @@ List<DataPointVO> deleteDataPoints(final int dataSourceId) {

//Clean permissions
for(DataPointVO vo : points) {
permissionDao.permissionDeleted(vo.getReadPermission(), vo.getSetPermission());
permissionService.permissionDeleted(vo.getReadPermission());
permissionService.permissionDeleted(vo.getSetPermission());
permissionService.permissionDeleted(vo.getEditPermission());
}

}
Expand Down Expand Up @@ -360,9 +381,9 @@ public DataPointSummary getSummary(String xid) {
summary.setName(rs.getString(3));
summary.setDataSourceId(rs.getInt(4));
summary.setDeviceName(rs.getString(5));
summary.setReadPermission(permissionDao.get(rs.getInt(6)));
summary.setEditPermission(permissionDao.get(rs.getInt(7)));
summary.setSetPermission(permissionDao.get(rs.getInt(8)));
summary.setReadPermission(permissionService.get(rs.getInt(6)));
summary.setEditPermission(permissionService.get(rs.getInt(7)));
summary.setSetPermission(permissionService.get(rs.getInt(8)));
return summary;
}else {
return null;
Expand Down Expand Up @@ -573,9 +594,9 @@ public DataPointVO mapRow(ResultSet rs, int rowNum) throws SQLException {

@Override
public void savePreRelationalData(DataPointVO existing, DataPointVO vo) {
permissionDao.permissionId(vo.getReadPermission());
permissionDao.permissionId(vo.getEditPermission());
permissionDao.permissionId(vo.getSetPermission());
permissionService.permissionId(vo.getReadPermission());
permissionService.permissionId(vo.getEditPermission());
permissionService.permissionId(vo.getSetPermission());
}

@Override
Expand All @@ -601,13 +622,13 @@ public void saveRelationalData(DataPointVO existing, DataPointVO vo) {

if(existing != null) {
if(!existing.getReadPermission().equals(vo.getReadPermission())) {
permissionDao.permissionDeleted(existing.getReadPermission());
permissionService.permissionDeleted(existing.getReadPermission());
}
if(!existing.getEditPermission().equals(vo.getEditPermission())) {
permissionDao.permissionDeleted(existing.getEditPermission());
permissionService.permissionDeleted(existing.getEditPermission());
}
if(!existing.getSetPermission().equals(vo.getSetPermission())) {
permissionDao.permissionDeleted(existing.getSetPermission());
permissionService.permissionDeleted(existing.getSetPermission());
}
}
}
Expand All @@ -623,9 +644,9 @@ public void loadRelationalData(DataPointVO vo) {
vo.setTags(dataPointTagsDao.getTagsForDataPointId(vo.getId()));

//Populate permissions
vo.setReadPermission(permissionDao.get(vo.getReadPermission().getId()));
vo.setEditPermission(permissionDao.get(vo.getEditPermission().getId()));
vo.setSetPermission(permissionDao.get(vo.getSetPermission().getId()));
vo.setReadPermission(permissionService.get(vo.getReadPermission().getId()));
vo.setEditPermission(permissionService.get(vo.getEditPermission().getId()));
vo.setSetPermission(permissionService.get(vo.getSetPermission().getId()));

DataSourceDefinition<? extends DataSourceVO> def = ModuleRegistry.getDataSourceDefinition(vo.getPointLocator().getDataSourceType());
if(def != null) {
Expand Down Expand Up @@ -657,7 +678,9 @@ public void deleteRelationalData(DataPointVO vo) {
@Override
public void deletePostRelationalData(DataPointVO vo) {
//Clean permissions
permissionDao.permissionDeleted(vo.getReadPermission(), vo.getEditPermission(), vo.getSetPermission());
permissionService.permissionDeleted(vo.getReadPermission());
permissionService.permissionDeleted(vo.getEditPermission());
permissionService.permissionDeleted(vo.getSetPermission());
}

@Override
Expand Down
Loading