From 0669db52005d13cb05d003778ae451daaf9d78f9 Mon Sep 17 00:00:00 2001 From: "Patel, Parth" Date: Wed, 24 Jun 2026 13:44:05 -0400 Subject: [PATCH 1/9] feat: Add Role-Based Feature Visibility system (App Profiles + Platform Profiles) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Part A — App Profiles: - AbstractSecurityUtils: 4 new tables (APP_PROFILE, APP_FEATURE, APP_PROFILE_FEATURE, APP_USER_PROFILE) bootstrapped in initialize() - AppProfileUtils: full CRUD for profiles, features, profile-feature assignments, user-profile assignments, and feature evaluation (checkFeature, getUserFeatures — enabled-only, fail-closed) - 16 reactors in prerna.reactor.appprofile covering all Pixel operations - SecurityProjectUtils: cascade deleteUserProfile calls in removeProjectUser, removeExpiredProjectUser, and removeProjectUsers to prevent stale assignments Part B — Platform Profiles: - AbstractSecurityUtils: 3 new tables (PLATFORM_PROFILE, PLATFORM_PROFILE_FEATURE, PLATFORM_USER_PROFILE) - PlatformProfileUtils: CRUD for platform profiles, predefined nav-key feature toggles, user assignments, and getUserFeatures (fail-open for unassigned users) - 9 reactors in prerna.reactor.platformprofile Co-Authored-By: Claude Sonnet 4.6 --- .../auth/utils/AbstractSecurityUtils.java | 192 +++++ src/prerna/auth/utils/AppProfileUtils.java | 709 ++++++++++++++++++ .../auth/utils/PlatformProfileUtils.java | 371 +++++++++ .../auth/utils/SecurityProjectUtils.java | 8 + .../appprofile/AssignUserProfileReactor.java | 64 ++ .../appprofile/CheckFeatureReactor.java | 61 ++ .../appprofile/CreateAppFeatureReactor.java | 66 ++ .../appprofile/CreateAppProfileReactor.java | 67 ++ .../appprofile/DeleteAppFeatureReactor.java | 63 ++ .../appprofile/DeleteAppProfileReactor.java | 63 ++ .../appprofile/GetAppFeaturesReactor.java | 63 ++ .../appprofile/GetAppProfilesReactor.java | 63 ++ .../appprofile/GetProfileFeaturesReactor.java | 64 ++ .../appprofile/GetProfileUsersReactor.java | 64 ++ .../appprofile/GetUserFeaturesReactor.java | 63 ++ .../appprofile/GetUserProfileReactor.java | 63 ++ .../appprofile/RemoveUserProfileReactor.java | 63 ++ .../appprofile/SetProfileFeatureReactor.java | 65 ++ .../appprofile/UpdateAppFeatureReactor.java | 65 ++ .../appprofile/UpdateAppProfileReactor.java | 67 ++ .../AssignUserPlatformProfileReactor.java | 62 ++ .../CreatePlatformProfileReactor.java | 64 ++ .../DeletePlatformProfileReactor.java | 61 ++ .../GetPlatformFeaturesReactor.java | 61 ++ .../GetPlatformProfilesReactor.java | 61 ++ .../GetUserPlatformFeaturesReactor.java | 58 ++ .../RemoveUserPlatformProfileReactor.java | 61 ++ .../SetPlatformFeatureReactor.java | 63 ++ .../UpdatePlatformProfileReactor.java | 63 ++ 29 files changed, 2858 insertions(+) create mode 100644 src/prerna/auth/utils/AppProfileUtils.java create mode 100644 src/prerna/auth/utils/PlatformProfileUtils.java create mode 100644 src/prerna/reactor/appprofile/AssignUserProfileReactor.java create mode 100644 src/prerna/reactor/appprofile/CheckFeatureReactor.java create mode 100644 src/prerna/reactor/appprofile/CreateAppFeatureReactor.java create mode 100644 src/prerna/reactor/appprofile/CreateAppProfileReactor.java create mode 100644 src/prerna/reactor/appprofile/DeleteAppFeatureReactor.java create mode 100644 src/prerna/reactor/appprofile/DeleteAppProfileReactor.java create mode 100644 src/prerna/reactor/appprofile/GetAppFeaturesReactor.java create mode 100644 src/prerna/reactor/appprofile/GetAppProfilesReactor.java create mode 100644 src/prerna/reactor/appprofile/GetProfileFeaturesReactor.java create mode 100644 src/prerna/reactor/appprofile/GetProfileUsersReactor.java create mode 100644 src/prerna/reactor/appprofile/GetUserFeaturesReactor.java create mode 100644 src/prerna/reactor/appprofile/GetUserProfileReactor.java create mode 100644 src/prerna/reactor/appprofile/RemoveUserProfileReactor.java create mode 100644 src/prerna/reactor/appprofile/SetProfileFeatureReactor.java create mode 100644 src/prerna/reactor/appprofile/UpdateAppFeatureReactor.java create mode 100644 src/prerna/reactor/appprofile/UpdateAppProfileReactor.java create mode 100644 src/prerna/reactor/platformprofile/AssignUserPlatformProfileReactor.java create mode 100644 src/prerna/reactor/platformprofile/CreatePlatformProfileReactor.java create mode 100644 src/prerna/reactor/platformprofile/DeletePlatformProfileReactor.java create mode 100644 src/prerna/reactor/platformprofile/GetPlatformFeaturesReactor.java create mode 100644 src/prerna/reactor/platformprofile/GetPlatformProfilesReactor.java create mode 100644 src/prerna/reactor/platformprofile/GetUserPlatformFeaturesReactor.java create mode 100644 src/prerna/reactor/platformprofile/RemoveUserPlatformProfileReactor.java create mode 100644 src/prerna/reactor/platformprofile/SetPlatformFeatureReactor.java create mode 100644 src/prerna/reactor/platformprofile/UpdatePlatformProfileReactor.java diff --git a/src/prerna/auth/utils/AbstractSecurityUtils.java b/src/prerna/auth/utils/AbstractSecurityUtils.java index 4901a1ee69f..c83eab7b031 100644 --- a/src/prerna/auth/utils/AbstractSecurityUtils.java +++ b/src/prerna/auth/utils/AbstractSecurityUtils.java @@ -2414,6 +2414,198 @@ public static void initialize() throws Exception { } } + // APP_PROFILE — named profiles per app + colNames = new String[] { "PROFILE_ID", "APP_ID", "PROFILE_NAME", "DESCRIPTION", "IS_DEFAULT", "CREATED_BY", "CREATED_AT" }; + types = new String[] { "VARCHAR(255)", "VARCHAR(255)", "VARCHAR(255)", "VARCHAR(2000)", BOOLEAN_DATATYPE_NAME, "VARCHAR(255)", TIMESTAMP_DATATYPE_NAME }; + defaultValues = new Object[] { null, null, null, null, false, null, null }; + if (allowIfExistsTable) { + String sql = queryUtil.createTableIfNotExistsWithDefaults("APP_PROFILE", colNames, types, defaultValues); + classLogger.info("Running sql {}", sql); + securityDb.insertData(sql); + } else { + if (!queryUtil.tableExists(conn, "APP_PROFILE", database, schema)) { + String sql = queryUtil.createTable("APP_PROFILE", colNames, types); + classLogger.info("Running sql {}", sql); + securityDb.insertData(sql); + } + } + { + List allCols = queryUtil.getTableColumns(conn, "APP_PROFILE", database, schema); + for (int i = 0; i < colNames.length; i++) { + String col = colNames[i]; + if (!allCols.contains(col) && !allCols.contains(col.toLowerCase())) { + classLogger.info("Column '{}' is not present in current list of columns: {}", col, allCols); + String addColumnSql = queryUtil.alterTableAddColumn("APP_PROFILE", col, types[i]); + classLogger.info("Running sql {}", addColumnSql); + securityDb.insertData(addColumnSql); + } + } + } + + // APP_FEATURE — feature key definitions per app + colNames = new String[] { "FEATURE_ID", "APP_ID", "FEATURE_KEY", "DESCRIPTION", "CREATED_BY", "CREATED_AT" }; + types = new String[] { "VARCHAR(255)", "VARCHAR(255)", "VARCHAR(255)", "VARCHAR(2000)", "VARCHAR(255)", TIMESTAMP_DATATYPE_NAME }; + if (allowIfExistsTable) { + String sql = queryUtil.createTableIfNotExists("APP_FEATURE", colNames, types); + classLogger.info("Running sql {}", sql); + securityDb.insertData(sql); + } else { + if (!queryUtil.tableExists(conn, "APP_FEATURE", database, schema)) { + String sql = queryUtil.createTable("APP_FEATURE", colNames, types); + classLogger.info("Running sql {}", sql); + securityDb.insertData(sql); + } + } + { + List allCols = queryUtil.getTableColumns(conn, "APP_FEATURE", database, schema); + for (int i = 0; i < colNames.length; i++) { + String col = colNames[i]; + if (!allCols.contains(col) && !allCols.contains(col.toLowerCase())) { + classLogger.info("Column '{}' is not present in current list of columns: {}", col, allCols); + String addColumnSql = queryUtil.alterTableAddColumn("APP_FEATURE", col, types[i]); + classLogger.info("Running sql {}", addColumnSql); + securityDb.insertData(addColumnSql); + } + } + } + + // APP_PROFILE_FEATURE — which features are ON per profile (missing row = OFF) + colNames = new String[] { "APP_ID", "PROFILE_ID", "FEATURE_ID", "ENABLED" }; + types = new String[] { "VARCHAR(255)", "VARCHAR(255)", "VARCHAR(255)", BOOLEAN_DATATYPE_NAME }; + defaultValues = new Object[] { null, null, null, true }; + if (allowIfExistsTable) { + String sql = queryUtil.createTableIfNotExistsWithDefaults("APP_PROFILE_FEATURE", colNames, types, defaultValues); + classLogger.info("Running sql {}", sql); + securityDb.insertData(sql); + } else { + if (!queryUtil.tableExists(conn, "APP_PROFILE_FEATURE", database, schema)) { + String sql = queryUtil.createTable("APP_PROFILE_FEATURE", colNames, types); + classLogger.info("Running sql {}", sql); + securityDb.insertData(sql); + } + } + { + List allCols = queryUtil.getTableColumns(conn, "APP_PROFILE_FEATURE", database, schema); + for (int i = 0; i < colNames.length; i++) { + String col = colNames[i]; + if (!allCols.contains(col) && !allCols.contains(col.toLowerCase())) { + classLogger.info("Column '{}' is not present in current list of columns: {}", col, allCols); + String addColumnSql = queryUtil.alterTableAddColumn("APP_PROFILE_FEATURE", col, types[i]); + classLogger.info("Running sql {}", addColumnSql); + securityDb.insertData(addColumnSql); + } + } + } + + // APP_USER_PROFILE — user-to-profile assignment per app (one per user per app) + colNames = new String[] { "APP_ID", "USER_ID", "PROFILE_ID", "ASSIGNED_BY", "ASSIGNED_AT" }; + types = new String[] { "VARCHAR(255)", "VARCHAR(255)", "VARCHAR(255)", "VARCHAR(255)", TIMESTAMP_DATATYPE_NAME }; + if (allowIfExistsTable) { + String sql = queryUtil.createTableIfNotExists("APP_USER_PROFILE", colNames, types); + classLogger.info("Running sql {}", sql); + securityDb.insertData(sql); + } else { + if (!queryUtil.tableExists(conn, "APP_USER_PROFILE", database, schema)) { + String sql = queryUtil.createTable("APP_USER_PROFILE", colNames, types); + classLogger.info("Running sql {}", sql); + securityDb.insertData(sql); + } + } + { + List allCols = queryUtil.getTableColumns(conn, "APP_USER_PROFILE", database, schema); + for (int i = 0; i < colNames.length; i++) { + String col = colNames[i]; + if (!allCols.contains(col) && !allCols.contains(col.toLowerCase())) { + classLogger.info("Column '{}' is not present in current list of columns: {}", col, allCols); + String addColumnSql = queryUtil.alterTableAddColumn("APP_USER_PROFILE", col, types[i]); + classLogger.info("Running sql {}", addColumnSql); + securityDb.insertData(addColumnSql); + } + } + } + + // PLATFORM_PROFILE — named platform-level profiles + colNames = new String[] { "PROFILE_ID", "PROFILE_NAME", "DESCRIPTION", "CREATED_BY", "CREATED_AT" }; + types = new String[] { "VARCHAR(255)", "VARCHAR(255)", "VARCHAR(2000)", "VARCHAR(255)", TIMESTAMP_DATATYPE_NAME }; + if (allowIfExistsTable) { + String sql = queryUtil.createTableIfNotExists("PLATFORM_PROFILE", colNames, types); + classLogger.info("Running sql {}", sql); + securityDb.insertData(sql); + } else { + if (!queryUtil.tableExists(conn, "PLATFORM_PROFILE", database, schema)) { + String sql = queryUtil.createTable("PLATFORM_PROFILE", colNames, types); + classLogger.info("Running sql {}", sql); + securityDb.insertData(sql); + } + } + { + List allCols = queryUtil.getTableColumns(conn, "PLATFORM_PROFILE", database, schema); + for (int i = 0; i < colNames.length; i++) { + String col = colNames[i]; + if (!allCols.contains(col) && !allCols.contains(col.toLowerCase())) { + classLogger.info("Column '{}' is not present in current list of columns: {}", col, allCols); + String addColumnSql = queryUtil.alterTableAddColumn("PLATFORM_PROFILE", col, types[i]); + classLogger.info("Running sql {}", addColumnSql); + securityDb.insertData(addColumnSql); + } + } + } + + // PLATFORM_PROFILE_FEATURE — predefined nav keys ON/OFF per platform profile + colNames = new String[] { "PROFILE_ID", "FEATURE_KEY", "ENABLED" }; + types = new String[] { "VARCHAR(255)", "VARCHAR(100)", BOOLEAN_DATATYPE_NAME }; + defaultValues = new Object[] { null, null, true }; + if (allowIfExistsTable) { + String sql = queryUtil.createTableIfNotExistsWithDefaults("PLATFORM_PROFILE_FEATURE", colNames, types, defaultValues); + classLogger.info("Running sql {}", sql); + securityDb.insertData(sql); + } else { + if (!queryUtil.tableExists(conn, "PLATFORM_PROFILE_FEATURE", database, schema)) { + String sql = queryUtil.createTable("PLATFORM_PROFILE_FEATURE", colNames, types); + classLogger.info("Running sql {}", sql); + securityDb.insertData(sql); + } + } + { + List allCols = queryUtil.getTableColumns(conn, "PLATFORM_PROFILE_FEATURE", database, schema); + for (int i = 0; i < colNames.length; i++) { + String col = colNames[i]; + if (!allCols.contains(col) && !allCols.contains(col.toLowerCase())) { + classLogger.info("Column '{}' is not present in current list of columns: {}", col, allCols); + String addColumnSql = queryUtil.alterTableAddColumn("PLATFORM_PROFILE_FEATURE", col, types[i]); + classLogger.info("Running sql {}", addColumnSql); + securityDb.insertData(addColumnSql); + } + } + } + + // PLATFORM_USER_PROFILE — user-to-platform-profile assignment (one per user) + colNames = new String[] { "USER_ID", "PROFILE_ID", "ASSIGNED_BY", "ASSIGNED_AT" }; + types = new String[] { "VARCHAR(255)", "VARCHAR(255)", "VARCHAR(255)", TIMESTAMP_DATATYPE_NAME }; + if (allowIfExistsTable) { + String sql = queryUtil.createTableIfNotExists("PLATFORM_USER_PROFILE", colNames, types); + classLogger.info("Running sql {}", sql); + securityDb.insertData(sql); + } else { + if (!queryUtil.tableExists(conn, "PLATFORM_USER_PROFILE", database, schema)) { + String sql = queryUtil.createTable("PLATFORM_USER_PROFILE", colNames, types); + classLogger.info("Running sql {}", sql); + securityDb.insertData(sql); + } + } + { + List allCols = queryUtil.getTableColumns(conn, "PLATFORM_USER_PROFILE", database, schema); + for (int i = 0; i < colNames.length; i++) { + String col = colNames[i]; + if (!allCols.contains(col) && !allCols.contains(col.toLowerCase())) { + classLogger.info("Column '{}' is not present in current list of columns: {}", col, allCols); + String addColumnSql = queryUtil.alterTableAddColumn("PLATFORM_USER_PROFILE", col, types[i]); + classLogger.info("Running sql {}", addColumnSql); + securityDb.insertData(addColumnSql); + } + } + } + if (!conn.getAutoCommit()) { conn.commit(); } diff --git a/src/prerna/auth/utils/AppProfileUtils.java b/src/prerna/auth/utils/AppProfileUtils.java new file mode 100644 index 00000000000..6d6a748c1a3 --- /dev/null +++ b/src/prerna/auth/utils/AppProfileUtils.java @@ -0,0 +1,709 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.auth.utils; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import prerna.auth.AccessToken; +import prerna.auth.User; +import prerna.engine.api.IRDBMSEngine; +import prerna.engine.api.IRawSelectWrapper; +import prerna.query.querystruct.SelectQueryStruct; +import prerna.query.querystruct.filters.SimpleQueryFilter; +import prerna.query.querystruct.selectors.QueryColumnSelector; +import prerna.rdf.engine.wrappers.WrapperManager; +import prerna.util.ConnectionUtils; +import prerna.util.SystemEngineRegistry; +import prerna.util.Utility; + +public class AppProfileUtils { + + private static final Logger classLogger = LogManager.getLogger(AppProfileUtils.class); + private static final String FEATURE_KEY_PATTERN = "^[a-zA-Z0-9\\-]+$"; + private static final int FEATURE_KEY_MAX_LENGTH = 100; + + private AppProfileUtils() { + } + + // ─── Permission checks ────────────────────────────────────────────────── + + public static boolean canManageProfiles(User user, String appId) { + if (!appExists(appId)) { + throw new IllegalArgumentException("App not found: " + appId); + } + if (SecurityAdminUtils.userIsAdmin(user)) return true; + if (SecurityProjectUtils.userIsOwner(user, appId)) return true; + if (SecurityProjectUtils.userCanEditProject(user, appId)) return true; + return false; + } + + public static boolean canEvaluateFeatures(User user, String appId) { + return SecurityProjectUtils.userCanViewProject(user, appId); + } + + private static boolean appExists(String appId) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + SelectQueryStruct qs = new SelectQueryStruct(); + qs.addSelector(new QueryColumnSelector("PROJECTPERMISSION__PROJECTID")); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("PROJECTPERMISSION__PROJECTID", "==", appId)); + try (IRawSelectWrapper wrapper = WrapperManager.getInstance().getRawWrapper(securityDb, qs)) { + return wrapper.hasNext(); + } catch (Exception e) { + classLogger.error("Error checking app existence", e); + return false; + } + } + + // ─── Profile CRUD ─────────────────────────────────────────────────────── + + public static Map createProfile(String appId, String name, String description, + boolean isDefault, User user) { + if (name == null || name.trim().isEmpty()) { + throw new IllegalArgumentException("Profile name cannot be blank."); + } + if (name.trim().length() > 100) { + throw new IllegalArgumentException("Profile name cannot exceed 100 characters."); + } + String profileName = name.trim(); + String profileId = UUID.randomUUID().toString(); + String actorId = getUserId(user); + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + + if (isDefault) { + clearDefaultProfile(securityDb, appId); + } + + PreparedStatement ps = null; + try { + ps = securityDb.getPreparedStatement( + "INSERT INTO APP_PROFILE (PROFILE_ID, APP_ID, PROFILE_NAME, DESCRIPTION, IS_DEFAULT, CREATED_BY, CREATED_AT) VALUES (?,?,?,?,?,?,?)"); + int i = 1; + ps.setString(i++, profileId); + ps.setString(i++, appId); + ps.setString(i++, profileName); + ps.setString(i++, description); + ps.setBoolean(i++, isDefault); + ps.setString(i++, actorId); + ps.setTimestamp(i++, Utility.getCurrentSqlTimestampUTC()); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) { + ps.getConnection().commit(); + } + } catch (SQLException e) { + classLogger.error("Failed to create app profile", e); + throw new IllegalArgumentException("An error occurred creating the app profile."); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + + Map result = new HashMap<>(); + result.put("profileId", profileId); + result.put("profileName", profileName); + result.put("description", description); + result.put("isDefault", isDefault); + return result; + } + + public static void updateProfile(String appId, String profileId, String name, String description, + Boolean isDefault, User user) { + if (name != null) { + if (name.trim().isEmpty()) throw new IllegalArgumentException("Profile name cannot be blank."); + if (name.trim().length() > 100) throw new IllegalArgumentException("Profile name cannot exceed 100 characters."); + } + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + if (isDefault != null && isDefault) { + clearDefaultProfile(securityDb, appId); + } + StringBuilder sb = new StringBuilder("UPDATE APP_PROFILE SET"); + List params = new ArrayList<>(); + if (name != null) { sb.append(" PROFILE_NAME=?,"); params.add(name.trim()); } + if (description != null) { sb.append(" DESCRIPTION=?,"); params.add(description); } + if (isDefault != null) { sb.append(" IS_DEFAULT=?,"); params.add(isDefault); } + if (params.isEmpty()) return; + sb.setLength(sb.length() - 1); + sb.append(" WHERE PROFILE_ID=? AND APP_ID=?"); + params.add(profileId); + params.add(appId); + + PreparedStatement ps = null; + try { + ps = securityDb.getPreparedStatement(sb.toString()); + for (int i = 0; i < params.size(); i++) { + Object val = params.get(i); + if (val instanceof Boolean) { + ps.setBoolean(i + 1, (Boolean) val); + } else { + ps.setString(i + 1, (String) val); + } + } + ps.execute(); + if (!ps.getConnection().getAutoCommit()) { + ps.getConnection().commit(); + } + } catch (SQLException e) { + classLogger.error("Failed to update app profile", e); + throw new IllegalArgumentException("An error occurred updating the app profile."); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + } + + public static void deleteProfile(String appId, String profileId, User user) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + int count = getAssignedUserCount(securityDb, appId, profileId); + if (count > 0) { + throw new IllegalArgumentException( + "Cannot delete: " + count + " user(s) are assigned to this profile. Reassign them first."); + } + PreparedStatement ps = null; + try { + ps = securityDb.getPreparedStatement("DELETE FROM APP_PROFILE WHERE PROFILE_ID=? AND APP_ID=?"); + ps.setString(1, profileId); + ps.setString(2, appId); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to delete app profile", e); + throw new IllegalArgumentException("An error occurred deleting the app profile."); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + ps = null; + try { + ps = securityDb.getPreparedStatement("DELETE FROM APP_PROFILE_FEATURE WHERE PROFILE_ID=? AND APP_ID=?"); + ps.setString(1, profileId); + ps.setString(2, appId); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to cascade delete app profile features", e); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + } + + public static List> getProfiles(String appId) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + List> profiles = new ArrayList<>(); + SelectQueryStruct qs = new SelectQueryStruct(); + qs.addSelector(new QueryColumnSelector("APP_PROFILE__PROFILE_ID")); + qs.addSelector(new QueryColumnSelector("APP_PROFILE__PROFILE_NAME")); + qs.addSelector(new QueryColumnSelector("APP_PROFILE__DESCRIPTION")); + qs.addSelector(new QueryColumnSelector("APP_PROFILE__IS_DEFAULT")); + qs.addSelector(new QueryColumnSelector("APP_PROFILE__CREATED_BY")); + qs.addSelector(new QueryColumnSelector("APP_PROFILE__CREATED_AT")); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE__APP_ID", "==", appId)); + qs.addOrderBy("APP_PROFILE__PROFILE_NAME", "ASC"); + try (IRawSelectWrapper wrapper = WrapperManager.getInstance().getRawWrapper(securityDb, qs)) { + while (wrapper.hasNext()) { + Object[] row = wrapper.next().getValues(); + Map profile = new HashMap<>(); + profile.put("profileId", row[0]); + profile.put("profileName", row[1]); + profile.put("description", row[2]); + profile.put("isDefault", row[3]); + profile.put("createdBy", row[4]); + profile.put("createdAt", row[5]); + profile.put("userCount", getAssignedUserCount(securityDb, appId, (String) row[0])); + profiles.add(profile); + } + } catch (Exception e) { + classLogger.error("Failed to get app profiles", e); + } + return profiles; + } + + // ─── Feature CRUD ─────────────────────────────────────────────────────── + + public static Map createFeature(String appId, String featureKey, + String description, User user) { + validateFeatureKey(featureKey); + String featureId = UUID.randomUUID().toString(); + String actorId = getUserId(user); + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + PreparedStatement ps = null; + try { + ps = securityDb.getPreparedStatement( + "INSERT INTO APP_FEATURE (FEATURE_ID, APP_ID, FEATURE_KEY, DESCRIPTION, CREATED_BY, CREATED_AT) VALUES (?,?,?,?,?,?)"); + int i = 1; + ps.setString(i++, featureId); + ps.setString(i++, appId); + ps.setString(i++, featureKey); + ps.setString(i++, description); + ps.setString(i++, actorId); + ps.setTimestamp(i++, Utility.getCurrentSqlTimestampUTC()); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to create app feature", e); + throw new IllegalArgumentException("An error occurred creating the feature."); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + Map result = new HashMap<>(); + result.put("featureId", featureId); + result.put("featureKey", featureKey); + result.put("description", description); + return result; + } + + public static void updateFeature(String appId, String featureId, String featureKey, + String description, User user) { + if (featureKey != null) validateFeatureKey(featureKey); + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + StringBuilder sb = new StringBuilder("UPDATE APP_FEATURE SET"); + List params = new ArrayList<>(); + if (featureKey != null) { sb.append(" FEATURE_KEY=?,"); params.add(featureKey); } + if (description != null) { sb.append(" DESCRIPTION=?,"); params.add(description); } + if (params.isEmpty()) return; + sb.setLength(sb.length() - 1); + sb.append(" WHERE FEATURE_ID=? AND APP_ID=?"); + params.add(featureId); + params.add(appId); + PreparedStatement ps = null; + try { + ps = securityDb.getPreparedStatement(sb.toString()); + for (int i = 0; i < params.size(); i++) { + ps.setString(i + 1, params.get(i)); + } + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to update app feature", e); + throw new IllegalArgumentException("An error occurred updating the feature."); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + } + + public static void deleteFeature(String appId, String featureId, User user) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + PreparedStatement ps = null; + try { + ps = securityDb.getPreparedStatement("DELETE FROM APP_FEATURE WHERE FEATURE_ID=? AND APP_ID=?"); + ps.setString(1, featureId); + ps.setString(2, appId); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to delete app feature", e); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + ps = null; + try { + ps = securityDb.getPreparedStatement("DELETE FROM APP_PROFILE_FEATURE WHERE FEATURE_ID=? AND APP_ID=?"); + ps.setString(1, featureId); + ps.setString(2, appId); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to cascade delete feature from profile mappings", e); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + } + + public static List> getFeatures(String appId) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + List> features = new ArrayList<>(); + SelectQueryStruct qs = new SelectQueryStruct(); + qs.addSelector(new QueryColumnSelector("APP_FEATURE__FEATURE_ID")); + qs.addSelector(new QueryColumnSelector("APP_FEATURE__FEATURE_KEY")); + qs.addSelector(new QueryColumnSelector("APP_FEATURE__DESCRIPTION")); + qs.addSelector(new QueryColumnSelector("APP_FEATURE__CREATED_BY")); + qs.addSelector(new QueryColumnSelector("APP_FEATURE__CREATED_AT")); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_FEATURE__APP_ID", "==", appId)); + qs.addOrderBy("APP_FEATURE__FEATURE_KEY", "ASC"); + try (IRawSelectWrapper wrapper = WrapperManager.getInstance().getRawWrapper(securityDb, qs)) { + while (wrapper.hasNext()) { + Object[] row = wrapper.next().getValues(); + Map feature = new HashMap<>(); + feature.put("featureId", row[0]); + feature.put("featureKey", row[1]); + feature.put("description", row[2]); + feature.put("createdBy", row[3]); + feature.put("createdAt", row[4]); + features.add(feature); + } + } catch (Exception e) { + classLogger.error("Failed to get app features", e); + } + return features; + } + + // ─── Profile-Feature Assignment ───────────────────────────────────────── + + public static void setProfileFeature(String appId, String profileId, String featureId, + boolean enabled, User user) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + PreparedStatement ps = null; + try { + ps = securityDb.getPreparedStatement( + "DELETE FROM APP_PROFILE_FEATURE WHERE APP_ID=? AND PROFILE_ID=? AND FEATURE_ID=?"); + ps.setString(1, appId); + ps.setString(2, profileId); + ps.setString(3, featureId); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to delete existing profile feature row", e); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + ps = null; + try { + ps = securityDb.getPreparedStatement( + "INSERT INTO APP_PROFILE_FEATURE (APP_ID, PROFILE_ID, FEATURE_ID, ENABLED) VALUES (?,?,?,?)"); + ps.setString(1, appId); + ps.setString(2, profileId); + ps.setString(3, featureId); + ps.setBoolean(4, enabled); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to insert profile feature", e); + throw new IllegalArgumentException("An error occurred setting the profile feature."); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + } + + public static List> getProfileFeatures(String appId, String profileId) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + List> results = new ArrayList<>(); + String sql = "SELECT f.FEATURE_ID, f.FEATURE_KEY, f.DESCRIPTION, pf.ENABLED " + + "FROM APP_FEATURE f " + + "LEFT JOIN APP_PROFILE_FEATURE pf ON f.FEATURE_ID = pf.FEATURE_ID " + + "AND f.APP_ID = pf.APP_ID AND pf.PROFILE_ID = ? " + + "WHERE f.APP_ID = ? ORDER BY f.FEATURE_KEY"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, profileId); + ps.setString(2, appId); + rs = ps.executeQuery(); + while (rs.next()) { + Map row = new HashMap<>(); + row.put("featureId", rs.getString("FEATURE_ID")); + row.put("featureKey", rs.getString("FEATURE_KEY")); + row.put("description", rs.getString("DESCRIPTION")); + Object enabledObj = rs.getObject("ENABLED"); + row.put("enabled", enabledObj != null && rs.getBoolean("ENABLED")); + results.add(row); + } + } catch (SQLException e) { + classLogger.error("Failed to get profile features", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + return results; + } + + // ─── User-Profile Assignment ──────────────────────────────────────────── + + public static void assignUserProfile(String appId, String userId, String profileId, User actor) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + String actorId = getUserId(actor); + PreparedStatement ps = null; + try { + ps = securityDb.getPreparedStatement("DELETE FROM APP_USER_PROFILE WHERE APP_ID=? AND USER_ID=?"); + ps.setString(1, appId); + ps.setString(2, userId); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to remove existing user profile assignment", e); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + ps = null; + try { + ps = securityDb.getPreparedStatement( + "INSERT INTO APP_USER_PROFILE (APP_ID, USER_ID, PROFILE_ID, ASSIGNED_BY, ASSIGNED_AT) VALUES (?,?,?,?,?)"); + ps.setString(1, appId); + ps.setString(2, userId); + ps.setString(3, profileId); + ps.setString(4, actorId); + ps.setTimestamp(5, Utility.getCurrentSqlTimestampUTC()); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to assign user profile", e); + throw new IllegalArgumentException("An error occurred assigning the user profile."); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + } + + public static void removeUserProfile(String appId, String userId) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + PreparedStatement ps = null; + try { + ps = securityDb.getPreparedStatement("DELETE FROM APP_USER_PROFILE WHERE APP_ID=? AND USER_ID=?"); + ps.setString(1, appId); + ps.setString(2, userId); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to remove user profile assignment", e); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + } + + public static Map getUserProfile(String appId, String userId) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + String sql = "SELECT p.PROFILE_ID, p.PROFILE_NAME FROM APP_USER_PROFILE up " + + "JOIN APP_PROFILE p ON up.PROFILE_ID = p.PROFILE_ID " + + "WHERE up.APP_ID = ? AND up.USER_ID = ?"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, appId); + ps.setString(2, userId); + rs = ps.executeQuery(); + if (rs.next()) { + Map result = new HashMap<>(); + result.put("profileId", rs.getString("PROFILE_ID")); + result.put("profileName", rs.getString("PROFILE_NAME")); + result.put("isExplicitAssignment", true); + return result; + } + } catch (SQLException e) { + classLogger.error("Failed to get user profile", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + return getDefaultProfile(securityDb, appId); + } + + public static List> getProfileUsers(String appId, String profileId) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + List> users = new ArrayList<>(); + String sql = "SELECT up.USER_ID, up.ASSIGNED_BY, up.ASSIGNED_AT " + + "FROM APP_USER_PROFILE up " + + "INNER JOIN PROJECTPERMISSION pp ON up.USER_ID = pp.USERID AND up.APP_ID = pp.PROJECTID " + + "WHERE up.APP_ID = ? AND up.PROFILE_ID = ?"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, appId); + ps.setString(2, profileId); + rs = ps.executeQuery(); + while (rs.next()) { + Map row = new HashMap<>(); + row.put("userId", rs.getString("USER_ID")); + row.put("assignedBy", rs.getString("ASSIGNED_BY")); + row.put("assignedAt", rs.getTimestamp("ASSIGNED_AT")); + users.add(row); + } + } catch (SQLException e) { + classLogger.error("Failed to get profile users", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + return users; + } + + // ─── Feature evaluation ────────────────────────────────────────────────── + + public static boolean checkFeature(String appId, String featureKey, User user) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + String featureId = resolveFeatureId(securityDb, appId, featureKey); + if (featureId == null) return false; + String userId = getUserId(user); + Map profile = getUserProfile(appId, userId); + if (profile == null) return false; + String profileId = (String) profile.get("profileId"); + return queryFeatureEnabled(securityDb, appId, profileId, featureId); + } + + public static Map getUserFeatures(String appId, User user) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + String userId = getUserId(user); + Map profile = getUserProfile(appId, userId); + if (profile == null) return new HashMap<>(); + String profileId = (String) profile.get("profileId"); + String profileName = (String) profile.get("profileName"); + boolean isDefaultProfile = !(Boolean) profile.getOrDefault("isExplicitAssignment", Boolean.TRUE); + + Map result = new HashMap<>(); + // Return only features with ENABLED=true — callers cannot infer what features exist but are hidden + String sql = "SELECT f.FEATURE_KEY, f.FEATURE_ID " + + "FROM APP_PROFILE_FEATURE pf " + + "JOIN APP_FEATURE f ON pf.FEATURE_ID = f.FEATURE_ID AND pf.APP_ID = f.APP_ID " + + "WHERE pf.APP_ID = ? AND pf.PROFILE_ID = ? AND pf.ENABLED = true"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, appId); + ps.setString(2, profileId); + rs = ps.executeQuery(); + while (rs.next()) { + Map featureInfo = new HashMap<>(); + featureInfo.put("featureId", rs.getString("FEATURE_ID")); + featureInfo.put("profileName", profileName); + featureInfo.put("isDefaultProfile", isDefaultProfile); + result.put(rs.getString("FEATURE_KEY"), featureInfo); + } + } catch (SQLException e) { + classLogger.error("Failed to get user features", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + return result; + } + + // ─── Private helpers ───────────────────────────────────────────────────── + + private static void validateFeatureKey(String key) { + if (key == null || key.isEmpty()) { + throw new IllegalArgumentException("Feature key cannot be blank."); + } + if (key.length() > FEATURE_KEY_MAX_LENGTH) { + throw new IllegalArgumentException("Feature key cannot exceed " + FEATURE_KEY_MAX_LENGTH + " characters."); + } + if (!key.matches(FEATURE_KEY_PATTERN)) { + throw new IllegalArgumentException( + "Feature key must contain only alphanumeric characters and hyphens."); + } + } + + private static void clearDefaultProfile(IRDBMSEngine securityDb, String appId) { + PreparedStatement ps = null; + try { + ps = securityDb.getPreparedStatement("UPDATE APP_PROFILE SET IS_DEFAULT=false WHERE APP_ID=?"); + ps.setString(1, appId); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to clear default profile", e); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + } + + private static int getAssignedUserCount(IRDBMSEngine securityDb, String appId, String profileId) { + String sql = "SELECT COUNT(*) FROM APP_USER_PROFILE WHERE APP_ID=? AND PROFILE_ID=?"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, appId); + ps.setString(2, profileId); + rs = ps.executeQuery(); + if (rs.next()) return rs.getInt(1); + } catch (SQLException e) { + classLogger.error("Failed to count profile users", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + return 0; + } + + private static Map getDefaultProfile(IRDBMSEngine securityDb, String appId) { + String sql = "SELECT PROFILE_ID, PROFILE_NAME FROM APP_PROFILE WHERE APP_ID=? AND IS_DEFAULT=true"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, appId); + rs = ps.executeQuery(); + if (rs.next()) { + Map result = new HashMap<>(); + result.put("profileId", rs.getString("PROFILE_ID")); + result.put("profileName", rs.getString("PROFILE_NAME")); + result.put("isExplicitAssignment", false); + return result; + } + } catch (SQLException e) { + classLogger.error("Failed to get default profile", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + return null; + } + + private static String resolveFeatureId(IRDBMSEngine securityDb, String appId, String featureKey) { + String sql = "SELECT FEATURE_ID FROM APP_FEATURE WHERE APP_ID=? AND FEATURE_KEY=?"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, appId); + ps.setString(2, featureKey); + rs = ps.executeQuery(); + if (rs.next()) return rs.getString("FEATURE_ID"); + } catch (SQLException e) { + classLogger.error("Failed to resolve feature ID", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + return null; + } + + private static boolean queryFeatureEnabled(IRDBMSEngine securityDb, String appId, String profileId, String featureId) { + String sql = "SELECT ENABLED FROM APP_PROFILE_FEATURE WHERE APP_ID=? AND PROFILE_ID=? AND FEATURE_ID=?"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, appId); + ps.setString(2, profileId); + ps.setString(3, featureId); + rs = ps.executeQuery(); + if (rs.next()) return rs.getBoolean("ENABLED"); + } catch (SQLException e) { + classLogger.error("Failed to check feature enabled", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + return false; + } + + static String getUserId(User user) { + if (user == null) return null; + AccessToken token = user.getAccessToken(user.getPrimaryLogin()); + return token != null ? token.getId() : null; + } +} diff --git a/src/prerna/auth/utils/PlatformProfileUtils.java b/src/prerna/auth/utils/PlatformProfileUtils.java new file mode 100644 index 00000000000..5a775374b43 --- /dev/null +++ b/src/prerna/auth/utils/PlatformProfileUtils.java @@ -0,0 +1,371 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.auth.utils; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import prerna.auth.User; +import prerna.engine.api.IRDBMSEngine; +import prerna.util.ConnectionUtils; +import prerna.util.SystemEngineRegistry; +import prerna.util.Utility; + +public class PlatformProfileUtils { + + private static final Logger classLogger = LogManager.getLogger(PlatformProfileUtils.class); + + public static final Set PREDEFINED_FEATURE_KEYS = Collections.unmodifiableSet( + new HashSet<>(Arrays.asList( + "nav.app-catalog", + "nav.build", + "nav.skills", + "nav.settings", + "nav.engine"))); + + private PlatformProfileUtils() { + } + + // ─── Permission check ──────────────────────────────────────────────────── + + public static boolean canManage(User user) { + return SecurityAdminUtils.userIsAdmin(user); + } + + // ─── Profile CRUD ──────────────────────────────────────────────────────── + + public static Map createProfile(String name, String description, User user) { + if (name == null || name.trim().isEmpty()) { + throw new IllegalArgumentException("Profile name cannot be blank."); + } + String profileId = UUID.randomUUID().toString(); + String actorId = AppProfileUtils.getUserId(user); + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + PreparedStatement ps = null; + try { + ps = securityDb.getPreparedStatement( + "INSERT INTO PLATFORM_PROFILE (PROFILE_ID, PROFILE_NAME, DESCRIPTION, CREATED_BY, CREATED_AT) VALUES (?,?,?,?,?)"); + int i = 1; + ps.setString(i++, profileId); + ps.setString(i++, name.trim()); + ps.setString(i++, description); + ps.setString(i++, actorId); + ps.setTimestamp(i++, Utility.getCurrentSqlTimestampUTC()); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to create platform profile", e); + throw new IllegalArgumentException("An error occurred creating the platform profile."); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + Map result = new HashMap<>(); + result.put("profileId", profileId); + result.put("profileName", name.trim()); + result.put("description", description); + return result; + } + + public static void updateProfile(String profileId, String name, String description, User user) { + if (name != null && name.trim().isEmpty()) { + throw new IllegalArgumentException("Profile name cannot be blank."); + } + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + StringBuilder sb = new StringBuilder("UPDATE PLATFORM_PROFILE SET"); + List params = new ArrayList<>(); + if (name != null) { sb.append(" PROFILE_NAME=?,"); params.add(name.trim()); } + if (description != null) { sb.append(" DESCRIPTION=?,"); params.add(description); } + if (params.isEmpty()) return; + sb.setLength(sb.length() - 1); + sb.append(" WHERE PROFILE_ID=?"); + params.add(profileId); + PreparedStatement ps = null; + try { + ps = securityDb.getPreparedStatement(sb.toString()); + for (int i = 0; i < params.size(); i++) { + ps.setString(i + 1, params.get(i)); + } + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to update platform profile", e); + throw new IllegalArgumentException("An error occurred updating the platform profile."); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + } + + public static void deleteProfile(String profileId, User user) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + int count = getAssignedUserCount(securityDb, profileId); + if (count > 0) { + throw new IllegalArgumentException( + "Cannot delete: " + count + " user(s) are assigned to this profile. Reassign them first."); + } + PreparedStatement ps = null; + try { + ps = securityDb.getPreparedStatement("DELETE FROM PLATFORM_PROFILE WHERE PROFILE_ID=?"); + ps.setString(1, profileId); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to delete platform profile", e); + throw new IllegalArgumentException("An error occurred deleting the platform profile."); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + ps = null; + try { + ps = securityDb.getPreparedStatement("DELETE FROM PLATFORM_PROFILE_FEATURE WHERE PROFILE_ID=?"); + ps.setString(1, profileId); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to cascade delete platform profile features", e); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + } + + public static List> getProfiles(User user) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + List> profiles = new ArrayList<>(); + String sql = "SELECT PROFILE_ID, PROFILE_NAME, DESCRIPTION, CREATED_BY, CREATED_AT " + + "FROM PLATFORM_PROFILE ORDER BY PROFILE_NAME"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + rs = ps.executeQuery(); + while (rs.next()) { + Map profile = new HashMap<>(); + String pid = rs.getString("PROFILE_ID"); + profile.put("profileId", pid); + profile.put("profileName", rs.getString("PROFILE_NAME")); + profile.put("description", rs.getString("DESCRIPTION")); + profile.put("createdBy", rs.getString("CREATED_BY")); + profile.put("createdAt", rs.getTimestamp("CREATED_AT")); + profile.put("userCount", getAssignedUserCount(securityDb, pid)); + profiles.add(profile); + } + } catch (SQLException e) { + classLogger.error("Failed to get platform profiles", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + return profiles; + } + + // ─── Profile-Feature Assignment ────────────────────────────────────────── + + public static void setProfileFeature(String profileId, String featureKey, boolean enabled, User user) { + if (!PREDEFINED_FEATURE_KEYS.contains(featureKey)) { + throw new IllegalArgumentException( + "Unknown platform feature key: " + featureKey + + ". Valid keys: " + PREDEFINED_FEATURE_KEYS); + } + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + PreparedStatement ps = null; + try { + ps = securityDb.getPreparedStatement( + "DELETE FROM PLATFORM_PROFILE_FEATURE WHERE PROFILE_ID=? AND FEATURE_KEY=?"); + ps.setString(1, profileId); + ps.setString(2, featureKey); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to delete existing platform feature row", e); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + ps = null; + try { + ps = securityDb.getPreparedStatement( + "INSERT INTO PLATFORM_PROFILE_FEATURE (PROFILE_ID, FEATURE_KEY, ENABLED) VALUES (?,?,?)"); + ps.setString(1, profileId); + ps.setString(2, featureKey); + ps.setBoolean(3, enabled); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to insert platform feature", e); + throw new IllegalArgumentException("An error occurred setting the platform feature."); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + } + + public static Map getProfileFeatures(String profileId) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + // Start with all keys disabled + Map result = new LinkedHashMap<>(); + for (String key : PREDEFINED_FEATURE_KEYS) { + result.put(key, Boolean.FALSE); + } + String sql = "SELECT FEATURE_KEY, ENABLED FROM PLATFORM_PROFILE_FEATURE WHERE PROFILE_ID=?"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, profileId); + rs = ps.executeQuery(); + while (rs.next()) { + String key = rs.getString("FEATURE_KEY"); + if (PREDEFINED_FEATURE_KEYS.contains(key)) { + result.put(key, rs.getBoolean("ENABLED")); + } + } + } catch (SQLException e) { + classLogger.error("Failed to get platform profile features", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + return result; + } + + // ─── User-Profile Assignment ───────────────────────────────────────────── + + public static void assignUserProfile(String userId, String profileId, User actor) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + String actorId = AppProfileUtils.getUserId(actor); + PreparedStatement ps = null; + try { + ps = securityDb.getPreparedStatement("DELETE FROM PLATFORM_USER_PROFILE WHERE USER_ID=?"); + ps.setString(1, userId); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to remove existing platform user profile", e); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + ps = null; + try { + ps = securityDb.getPreparedStatement( + "INSERT INTO PLATFORM_USER_PROFILE (USER_ID, PROFILE_ID, ASSIGNED_BY, ASSIGNED_AT) VALUES (?,?,?,?)"); + ps.setString(1, userId); + ps.setString(2, profileId); + ps.setString(3, actorId); + ps.setTimestamp(4, Utility.getCurrentSqlTimestampUTC()); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to assign platform user profile", e); + throw new IllegalArgumentException("An error occurred assigning the platform profile."); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + } + + public static void removeUserProfile(String userId, User actor) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + PreparedStatement ps = null; + try { + ps = securityDb.getPreparedStatement("DELETE FROM PLATFORM_USER_PROFILE WHERE USER_ID=?"); + ps.setString(1, userId); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to remove platform user profile", e); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + } + + // ─── Feature evaluation ────────────────────────────────────────────────── + + /** + * Returns all predefined platform feature keys with their enabled status for the + * calling user. If the user has no platform profile assigned, all keys are true + * (fail-open — user is already authenticated and admin-provisioned). + */ + public static Map getUserFeatures(User user) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + String userId = AppProfileUtils.getUserId(user); + String profileId = getAssignedProfileId(securityDb, userId); + if (profileId == null) { + // No profile assigned → all nav visible + Map all = new LinkedHashMap<>(); + for (String key : PREDEFINED_FEATURE_KEYS) { + all.put(key, Boolean.TRUE); + } + return all; + } + return getProfileFeatures(profileId); + } + + // ─── Private helpers ───────────────────────────────────────────────────── + + private static int getAssignedUserCount(IRDBMSEngine securityDb, String profileId) { + String sql = "SELECT COUNT(*) FROM PLATFORM_USER_PROFILE WHERE PROFILE_ID=?"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, profileId); + rs = ps.executeQuery(); + if (rs.next()) return rs.getInt(1); + } catch (SQLException e) { + classLogger.error("Failed to count platform profile users", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + return 0; + } + + private static String getAssignedProfileId(IRDBMSEngine securityDb, String userId) { + String sql = "SELECT PROFILE_ID FROM PLATFORM_USER_PROFILE WHERE USER_ID=?"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, userId); + rs = ps.executeQuery(); + if (rs.next()) return rs.getString("PROFILE_ID"); + } catch (SQLException e) { + classLogger.error("Failed to get platform user profile", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + return null; + } +} diff --git a/src/prerna/auth/utils/SecurityProjectUtils.java b/src/prerna/auth/utils/SecurityProjectUtils.java index adcbc388441..b47f3d45d9f 100644 --- a/src/prerna/auth/utils/SecurityProjectUtils.java +++ b/src/prerna/auth/utils/SecurityProjectUtils.java @@ -1751,6 +1751,8 @@ public static void removeProjectUser(User user, String existingUserId, String pr ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); } } + // cascade: remove any app profile assignment so no stale rows survive + AppProfileUtils.removeUserProfile(projectId, existingUserId); } /** @@ -1777,6 +1779,8 @@ public static void removeExpiredProjectUser(String userId, String projectId) { } finally { ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); } + // cascade: remove any app profile assignment so no stale rows survive + AppProfileUtils.removeUserProfile(projectId, userId); } /** @@ -4896,6 +4900,10 @@ public static void removeProjectUsers(User user, List existingUserIds, S } finally { ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); } + // cascade: remove any app profile assignments so no stale rows survive + for (String uid : existingUserIds) { + AppProfileUtils.removeUserProfile(projectId, uid); + } } /** diff --git a/src/prerna/reactor/appprofile/AssignUserProfileReactor.java b/src/prerna/reactor/appprofile/AssignUserProfileReactor.java new file mode 100644 index 00000000000..5f9ec1f00bd --- /dev/null +++ b/src/prerna/reactor/appprofile/AssignUserProfileReactor.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class AssignUserProfileReactor extends AbstractReactor { + + public AssignUserProfileReactor() { + this.keysToGet = new String[] { "app", "userId", "profileId" }; + this.keyRequired = new int[] { 1, 1, 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get("app"); + String userId = this.keyValue.get("userId"); + String profileId = this.keyValue.get("profileId"); + + if (!AppProfileUtils.canManageProfiles(user, appId)) { + throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); + } + AppProfileUtils.assignUserProfile(appId, userId, profileId, user); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("User assigned to profile.")); + return noun; + } + + @Override + public String getReactorDescription() { + return "Assign a user to a profile for an app."; + } +} diff --git a/src/prerna/reactor/appprofile/CheckFeatureReactor.java b/src/prerna/reactor/appprofile/CheckFeatureReactor.java new file mode 100644 index 00000000000..3732a95955a --- /dev/null +++ b/src/prerna/reactor/appprofile/CheckFeatureReactor.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class CheckFeatureReactor extends AbstractReactor { + + public CheckFeatureReactor() { + this.keysToGet = new String[] { "app", "featureKey" }; + this.keyRequired = new int[] { 1, 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get("app"); + String featureKey = this.keyValue.get("featureKey"); + + if (!AppProfileUtils.canEvaluateFeatures(user, appId)) { + throw new IllegalArgumentException("User does not have access to this app."); + } + boolean enabled = AppProfileUtils.checkFeature(appId, featureKey, user); + return new NounMetadata(enabled, PixelDataType.BOOLEAN); + } + + @Override + public String getReactorDescription() { + return "Check whether a feature is enabled for the calling user in an app."; + } +} diff --git a/src/prerna/reactor/appprofile/CreateAppFeatureReactor.java b/src/prerna/reactor/appprofile/CreateAppFeatureReactor.java new file mode 100644 index 00000000000..d4e7408ccf1 --- /dev/null +++ b/src/prerna/reactor/appprofile/CreateAppFeatureReactor.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import java.util.Map; + +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class CreateAppFeatureReactor extends AbstractReactor { + + public CreateAppFeatureReactor() { + this.keysToGet = new String[] { "app", "key", "description" }; + this.keyRequired = new int[] { 1, 1, 0 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get("app"); + String featureKey = this.keyValue.get("key"); + String description = this.keyValue.get("description"); + + if (!AppProfileUtils.canManageProfiles(user, appId)) { + throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); + } + Map result = AppProfileUtils.createFeature(appId, featureKey, description, user); + NounMetadata noun = new NounMetadata(result, PixelDataType.MAP); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Feature created.")); + return noun; + } + + @Override + public String getReactorDescription() { + return "Create a feature key for an app."; + } +} diff --git a/src/prerna/reactor/appprofile/CreateAppProfileReactor.java b/src/prerna/reactor/appprofile/CreateAppProfileReactor.java new file mode 100644 index 00000000000..ec13f3ba4fd --- /dev/null +++ b/src/prerna/reactor/appprofile/CreateAppProfileReactor.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import java.util.Map; + +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class CreateAppProfileReactor extends AbstractReactor { + + public CreateAppProfileReactor() { + this.keysToGet = new String[] { "app", "name", "description", "isDefault" }; + this.keyRequired = new int[] { 1, 1, 0, 0 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get("app"); + String name = this.keyValue.get("name"); + String description = this.keyValue.get("description"); + boolean isDefault = Boolean.parseBoolean(this.keyValue.get("isDefault")); + + if (!AppProfileUtils.canManageProfiles(user, appId)) { + throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); + } + Map result = AppProfileUtils.createProfile(appId, name, description, isDefault, user); + NounMetadata noun = new NounMetadata(result, PixelDataType.MAP); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Profile created.")); + return noun; + } + + @Override + public String getReactorDescription() { + return "Create a named profile for an app."; + } +} diff --git a/src/prerna/reactor/appprofile/DeleteAppFeatureReactor.java b/src/prerna/reactor/appprofile/DeleteAppFeatureReactor.java new file mode 100644 index 00000000000..40968e04d17 --- /dev/null +++ b/src/prerna/reactor/appprofile/DeleteAppFeatureReactor.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class DeleteAppFeatureReactor extends AbstractReactor { + + public DeleteAppFeatureReactor() { + this.keysToGet = new String[] { "app", "featureId" }; + this.keyRequired = new int[] { 1, 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get("app"); + String featureId = this.keyValue.get("featureId"); + + if (!AppProfileUtils.canManageProfiles(user, appId)) { + throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); + } + AppProfileUtils.deleteFeature(appId, featureId, user); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Feature deleted.")); + return noun; + } + + @Override + public String getReactorDescription() { + return "Delete a feature key from an app."; + } +} diff --git a/src/prerna/reactor/appprofile/DeleteAppProfileReactor.java b/src/prerna/reactor/appprofile/DeleteAppProfileReactor.java new file mode 100644 index 00000000000..41741010f69 --- /dev/null +++ b/src/prerna/reactor/appprofile/DeleteAppProfileReactor.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class DeleteAppProfileReactor extends AbstractReactor { + + public DeleteAppProfileReactor() { + this.keysToGet = new String[] { "app", "profileId" }; + this.keyRequired = new int[] { 1, 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get("app"); + String profileId = this.keyValue.get("profileId"); + + if (!AppProfileUtils.canManageProfiles(user, appId)) { + throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); + } + AppProfileUtils.deleteProfile(appId, profileId, user); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Profile deleted.")); + return noun; + } + + @Override + public String getReactorDescription() { + return "Delete a named profile from an app."; + } +} diff --git a/src/prerna/reactor/appprofile/GetAppFeaturesReactor.java b/src/prerna/reactor/appprofile/GetAppFeaturesReactor.java new file mode 100644 index 00000000000..24ffff4f5e4 --- /dev/null +++ b/src/prerna/reactor/appprofile/GetAppFeaturesReactor.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import java.util.List; +import java.util.Map; + +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class GetAppFeaturesReactor extends AbstractReactor { + + public GetAppFeaturesReactor() { + this.keysToGet = new String[] { "app" }; + this.keyRequired = new int[] { 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get("app"); + + if (!AppProfileUtils.canManageProfiles(user, appId)) { + throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); + } + List> features = AppProfileUtils.getFeatures(appId); + return new NounMetadata(features, PixelDataType.CUSTOM_DATA_STRUCTURE); + } + + @Override + public String getReactorDescription() { + return "Get all feature keys defined for an app."; + } +} diff --git a/src/prerna/reactor/appprofile/GetAppProfilesReactor.java b/src/prerna/reactor/appprofile/GetAppProfilesReactor.java new file mode 100644 index 00000000000..674e206b3b7 --- /dev/null +++ b/src/prerna/reactor/appprofile/GetAppProfilesReactor.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import java.util.List; +import java.util.Map; + +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class GetAppProfilesReactor extends AbstractReactor { + + public GetAppProfilesReactor() { + this.keysToGet = new String[] { "app" }; + this.keyRequired = new int[] { 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get("app"); + + if (!AppProfileUtils.canManageProfiles(user, appId)) { + throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); + } + List> profiles = AppProfileUtils.getProfiles(appId); + return new NounMetadata(profiles, PixelDataType.CUSTOM_DATA_STRUCTURE); + } + + @Override + public String getReactorDescription() { + return "Get all profiles defined for an app."; + } +} diff --git a/src/prerna/reactor/appprofile/GetProfileFeaturesReactor.java b/src/prerna/reactor/appprofile/GetProfileFeaturesReactor.java new file mode 100644 index 00000000000..3dc158629c6 --- /dev/null +++ b/src/prerna/reactor/appprofile/GetProfileFeaturesReactor.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import java.util.List; +import java.util.Map; + +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class GetProfileFeaturesReactor extends AbstractReactor { + + public GetProfileFeaturesReactor() { + this.keysToGet = new String[] { "app", "profileId" }; + this.keyRequired = new int[] { 1, 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get("app"); + String profileId = this.keyValue.get("profileId"); + + if (!AppProfileUtils.canManageProfiles(user, appId)) { + throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); + } + List> features = AppProfileUtils.getProfileFeatures(appId, profileId); + return new NounMetadata(features, PixelDataType.CUSTOM_DATA_STRUCTURE); + } + + @Override + public String getReactorDescription() { + return "Get all features with their enabled status for a profile."; + } +} diff --git a/src/prerna/reactor/appprofile/GetProfileUsersReactor.java b/src/prerna/reactor/appprofile/GetProfileUsersReactor.java new file mode 100644 index 00000000000..0ff41982a80 --- /dev/null +++ b/src/prerna/reactor/appprofile/GetProfileUsersReactor.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import java.util.List; +import java.util.Map; + +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class GetProfileUsersReactor extends AbstractReactor { + + public GetProfileUsersReactor() { + this.keysToGet = new String[] { "app", "profileId" }; + this.keyRequired = new int[] { 1, 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get("app"); + String profileId = this.keyValue.get("profileId"); + + if (!AppProfileUtils.canManageProfiles(user, appId)) { + throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); + } + List> users = AppProfileUtils.getProfileUsers(appId, profileId); + return new NounMetadata(users, PixelDataType.CUSTOM_DATA_STRUCTURE); + } + + @Override + public String getReactorDescription() { + return "Get all users assigned to a profile in an app."; + } +} diff --git a/src/prerna/reactor/appprofile/GetUserFeaturesReactor.java b/src/prerna/reactor/appprofile/GetUserFeaturesReactor.java new file mode 100644 index 00000000000..67e0e9aa3d9 --- /dev/null +++ b/src/prerna/reactor/appprofile/GetUserFeaturesReactor.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import java.util.Map; + +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class GetUserFeaturesReactor extends AbstractReactor { + + public GetUserFeaturesReactor() { + this.keysToGet = new String[] { "app" }; + this.keyRequired = new int[] { 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get("app"); + + if (!AppProfileUtils.canEvaluateFeatures(user, appId)) { + throw new IllegalArgumentException("User does not have access to this app."); + } + // Returns only enabled features — callers cannot infer what features exist but are hidden + Map features = AppProfileUtils.getUserFeatures(appId, user); + return new NounMetadata(features, PixelDataType.MAP); + } + + @Override + public String getReactorDescription() { + return "Get all enabled features for the calling user in an app."; + } +} diff --git a/src/prerna/reactor/appprofile/GetUserProfileReactor.java b/src/prerna/reactor/appprofile/GetUserProfileReactor.java new file mode 100644 index 00000000000..b1141e18182 --- /dev/null +++ b/src/prerna/reactor/appprofile/GetUserProfileReactor.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import java.util.Map; + +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class GetUserProfileReactor extends AbstractReactor { + + public GetUserProfileReactor() { + this.keysToGet = new String[] { "app", "userId" }; + this.keyRequired = new int[] { 1, 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get("app"); + String userId = this.keyValue.get("userId"); + + if (!AppProfileUtils.canManageProfiles(user, appId)) { + throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); + } + Map profile = AppProfileUtils.getUserProfile(appId, userId); + return new NounMetadata(profile, PixelDataType.MAP); + } + + @Override + public String getReactorDescription() { + return "Get the effective profile for a user in an app."; + } +} diff --git a/src/prerna/reactor/appprofile/RemoveUserProfileReactor.java b/src/prerna/reactor/appprofile/RemoveUserProfileReactor.java new file mode 100644 index 00000000000..6f91e41329e --- /dev/null +++ b/src/prerna/reactor/appprofile/RemoveUserProfileReactor.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class RemoveUserProfileReactor extends AbstractReactor { + + public RemoveUserProfileReactor() { + this.keysToGet = new String[] { "app", "userId" }; + this.keyRequired = new int[] { 1, 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get("app"); + String userId = this.keyValue.get("userId"); + + if (!AppProfileUtils.canManageProfiles(user, appId)) { + throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); + } + AppProfileUtils.removeUserProfile(appId, userId); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("User removed from profile.")); + return noun; + } + + @Override + public String getReactorDescription() { + return "Remove a user's profile assignment for an app."; + } +} diff --git a/src/prerna/reactor/appprofile/SetProfileFeatureReactor.java b/src/prerna/reactor/appprofile/SetProfileFeatureReactor.java new file mode 100644 index 00000000000..acdddc80293 --- /dev/null +++ b/src/prerna/reactor/appprofile/SetProfileFeatureReactor.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class SetProfileFeatureReactor extends AbstractReactor { + + public SetProfileFeatureReactor() { + this.keysToGet = new String[] { "app", "profileId", "featureId", "enabled" }; + this.keyRequired = new int[] { 1, 1, 1, 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get("app"); + String profileId = this.keyValue.get("profileId"); + String featureId = this.keyValue.get("featureId"); + boolean enabled = Boolean.parseBoolean(this.keyValue.get("enabled")); + + if (!AppProfileUtils.canManageProfiles(user, appId)) { + throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); + } + AppProfileUtils.setProfileFeature(appId, profileId, featureId, enabled, user); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Profile feature updated.")); + return noun; + } + + @Override + public String getReactorDescription() { + return "Enable or disable a feature for a profile."; + } +} diff --git a/src/prerna/reactor/appprofile/UpdateAppFeatureReactor.java b/src/prerna/reactor/appprofile/UpdateAppFeatureReactor.java new file mode 100644 index 00000000000..2a1a78cc3b1 --- /dev/null +++ b/src/prerna/reactor/appprofile/UpdateAppFeatureReactor.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class UpdateAppFeatureReactor extends AbstractReactor { + + public UpdateAppFeatureReactor() { + this.keysToGet = new String[] { "app", "featureId", "key", "description" }; + this.keyRequired = new int[] { 1, 1, 0, 0 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get("app"); + String featureId = this.keyValue.get("featureId"); + String featureKey = this.keyValue.get("key"); + String description = this.keyValue.get("description"); + + if (!AppProfileUtils.canManageProfiles(user, appId)) { + throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); + } + AppProfileUtils.updateFeature(appId, featureId, featureKey, description, user); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Feature updated.")); + return noun; + } + + @Override + public String getReactorDescription() { + return "Update a feature key for an app."; + } +} diff --git a/src/prerna/reactor/appprofile/UpdateAppProfileReactor.java b/src/prerna/reactor/appprofile/UpdateAppProfileReactor.java new file mode 100644 index 00000000000..994a1926751 --- /dev/null +++ b/src/prerna/reactor/appprofile/UpdateAppProfileReactor.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class UpdateAppProfileReactor extends AbstractReactor { + + public UpdateAppProfileReactor() { + this.keysToGet = new String[] { "app", "profileId", "name", "description", "isDefault" }; + this.keyRequired = new int[] { 1, 1, 0, 0, 0 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get("app"); + String profileId = this.keyValue.get("profileId"); + String name = this.keyValue.get("name"); + String description = this.keyValue.get("description"); + String isDefaultStr = this.keyValue.get("isDefault"); + Boolean isDefault = isDefaultStr != null ? Boolean.parseBoolean(isDefaultStr) : null; + + if (!AppProfileUtils.canManageProfiles(user, appId)) { + throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); + } + AppProfileUtils.updateProfile(appId, profileId, name, description, isDefault, user); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Profile updated.")); + return noun; + } + + @Override + public String getReactorDescription() { + return "Update a named profile for an app."; + } +} diff --git a/src/prerna/reactor/platformprofile/AssignUserPlatformProfileReactor.java b/src/prerna/reactor/platformprofile/AssignUserPlatformProfileReactor.java new file mode 100644 index 00000000000..ac29abf4185 --- /dev/null +++ b/src/prerna/reactor/platformprofile/AssignUserPlatformProfileReactor.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.platformprofile; + +import prerna.auth.User; +import prerna.auth.utils.PlatformProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class AssignUserPlatformProfileReactor extends AbstractReactor { + + public AssignUserPlatformProfileReactor() { + this.keysToGet = new String[] { "userId", "profileId" }; + this.keyRequired = new int[] { 1, 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + if (!PlatformProfileUtils.canManage(user)) { + throw new IllegalArgumentException("User must be an admin to manage platform profiles."); + } + String userId = this.keyValue.get("userId"); + String profileId = this.keyValue.get("profileId"); + PlatformProfileUtils.assignUserProfile(userId, profileId, user); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("User assigned to platform profile.")); + return noun; + } + + @Override + public String getReactorDescription() { + return "Assign a user to a platform profile."; + } +} diff --git a/src/prerna/reactor/platformprofile/CreatePlatformProfileReactor.java b/src/prerna/reactor/platformprofile/CreatePlatformProfileReactor.java new file mode 100644 index 00000000000..49e46ff524d --- /dev/null +++ b/src/prerna/reactor/platformprofile/CreatePlatformProfileReactor.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.platformprofile; + +import java.util.Map; + +import prerna.auth.User; +import prerna.auth.utils.PlatformProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class CreatePlatformProfileReactor extends AbstractReactor { + + public CreatePlatformProfileReactor() { + this.keysToGet = new String[] { "name", "description" }; + this.keyRequired = new int[] { 1, 0 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + if (!PlatformProfileUtils.canManage(user)) { + throw new IllegalArgumentException("User must be an admin to manage platform profiles."); + } + String name = this.keyValue.get("name"); + String description = this.keyValue.get("description"); + Map result = PlatformProfileUtils.createProfile(name, description, user); + NounMetadata noun = new NounMetadata(result, PixelDataType.MAP); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Platform profile created.")); + return noun; + } + + @Override + public String getReactorDescription() { + return "Create a platform profile."; + } +} diff --git a/src/prerna/reactor/platformprofile/DeletePlatformProfileReactor.java b/src/prerna/reactor/platformprofile/DeletePlatformProfileReactor.java new file mode 100644 index 00000000000..9c4563a0981 --- /dev/null +++ b/src/prerna/reactor/platformprofile/DeletePlatformProfileReactor.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.platformprofile; + +import prerna.auth.User; +import prerna.auth.utils.PlatformProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class DeletePlatformProfileReactor extends AbstractReactor { + + public DeletePlatformProfileReactor() { + this.keysToGet = new String[] { "profileId" }; + this.keyRequired = new int[] { 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + if (!PlatformProfileUtils.canManage(user)) { + throw new IllegalArgumentException("User must be an admin to manage platform profiles."); + } + String profileId = this.keyValue.get("profileId"); + PlatformProfileUtils.deleteProfile(profileId, user); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Platform profile deleted.")); + return noun; + } + + @Override + public String getReactorDescription() { + return "Delete a platform profile."; + } +} diff --git a/src/prerna/reactor/platformprofile/GetPlatformFeaturesReactor.java b/src/prerna/reactor/platformprofile/GetPlatformFeaturesReactor.java new file mode 100644 index 00000000000..df574374a21 --- /dev/null +++ b/src/prerna/reactor/platformprofile/GetPlatformFeaturesReactor.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.platformprofile; + +import java.util.Map; + +import prerna.auth.User; +import prerna.auth.utils.PlatformProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class GetPlatformFeaturesReactor extends AbstractReactor { + + public GetPlatformFeaturesReactor() { + this.keysToGet = new String[] { "profileId" }; + this.keyRequired = new int[] { 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + if (!PlatformProfileUtils.canManage(user)) { + throw new IllegalArgumentException("User must be an admin to manage platform profiles."); + } + String profileId = this.keyValue.get("profileId"); + Map features = PlatformProfileUtils.getProfileFeatures(profileId); + return new NounMetadata(features, PixelDataType.MAP); + } + + @Override + public String getReactorDescription() { + return "Get all predefined platform nav features with their enabled status for a profile."; + } +} diff --git a/src/prerna/reactor/platformprofile/GetPlatformProfilesReactor.java b/src/prerna/reactor/platformprofile/GetPlatformProfilesReactor.java new file mode 100644 index 00000000000..7a75ed68948 --- /dev/null +++ b/src/prerna/reactor/platformprofile/GetPlatformProfilesReactor.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.platformprofile; + +import java.util.List; +import java.util.Map; + +import prerna.auth.User; +import prerna.auth.utils.PlatformProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class GetPlatformProfilesReactor extends AbstractReactor { + + public GetPlatformProfilesReactor() { + this.keysToGet = new String[] {}; + this.keyRequired = new int[] {}; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + if (!PlatformProfileUtils.canManage(user)) { + throw new IllegalArgumentException("User must be an admin to manage platform profiles."); + } + List> profiles = PlatformProfileUtils.getProfiles(user); + return new NounMetadata(profiles, PixelDataType.CUSTOM_DATA_STRUCTURE); + } + + @Override + public String getReactorDescription() { + return "Get all platform profiles."; + } +} diff --git a/src/prerna/reactor/platformprofile/GetUserPlatformFeaturesReactor.java b/src/prerna/reactor/platformprofile/GetUserPlatformFeaturesReactor.java new file mode 100644 index 00000000000..94380762fbe --- /dev/null +++ b/src/prerna/reactor/platformprofile/GetUserPlatformFeaturesReactor.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.platformprofile; + +import java.util.Map; + +import prerna.auth.User; +import prerna.auth.utils.PlatformProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class GetUserPlatformFeaturesReactor extends AbstractReactor { + + public GetUserPlatformFeaturesReactor() { + this.keysToGet = new String[] {}; + this.keyRequired = new int[] {}; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + // Returns all predefined keys → true for unassigned users (fail-open at platform level) + Map features = PlatformProfileUtils.getUserFeatures(user); + return new NounMetadata(features, PixelDataType.MAP); + } + + @Override + public String getReactorDescription() { + return "Get platform nav feature visibility for the calling user. Unassigned users receive all features enabled."; + } +} diff --git a/src/prerna/reactor/platformprofile/RemoveUserPlatformProfileReactor.java b/src/prerna/reactor/platformprofile/RemoveUserPlatformProfileReactor.java new file mode 100644 index 00000000000..42fddabe6ce --- /dev/null +++ b/src/prerna/reactor/platformprofile/RemoveUserPlatformProfileReactor.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.platformprofile; + +import prerna.auth.User; +import prerna.auth.utils.PlatformProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class RemoveUserPlatformProfileReactor extends AbstractReactor { + + public RemoveUserPlatformProfileReactor() { + this.keysToGet = new String[] { "userId" }; + this.keyRequired = new int[] { 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + if (!PlatformProfileUtils.canManage(user)) { + throw new IllegalArgumentException("User must be an admin to manage platform profiles."); + } + String userId = this.keyValue.get("userId"); + PlatformProfileUtils.removeUserProfile(userId, user); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("User removed from platform profile.")); + return noun; + } + + @Override + public String getReactorDescription() { + return "Remove a user's platform profile assignment."; + } +} diff --git a/src/prerna/reactor/platformprofile/SetPlatformFeatureReactor.java b/src/prerna/reactor/platformprofile/SetPlatformFeatureReactor.java new file mode 100644 index 00000000000..d77acfc5e55 --- /dev/null +++ b/src/prerna/reactor/platformprofile/SetPlatformFeatureReactor.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.platformprofile; + +import prerna.auth.User; +import prerna.auth.utils.PlatformProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class SetPlatformFeatureReactor extends AbstractReactor { + + public SetPlatformFeatureReactor() { + this.keysToGet = new String[] { "profileId", "featureKey", "enabled" }; + this.keyRequired = new int[] { 1, 1, 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + if (!PlatformProfileUtils.canManage(user)) { + throw new IllegalArgumentException("User must be an admin to manage platform profiles."); + } + String profileId = this.keyValue.get("profileId"); + String featureKey = this.keyValue.get("featureKey"); + boolean enabled = Boolean.parseBoolean(this.keyValue.get("enabled")); + PlatformProfileUtils.setProfileFeature(profileId, featureKey, enabled, user); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Platform feature updated.")); + return noun; + } + + @Override + public String getReactorDescription() { + return "Enable or disable a predefined platform nav feature for a profile."; + } +} diff --git a/src/prerna/reactor/platformprofile/UpdatePlatformProfileReactor.java b/src/prerna/reactor/platformprofile/UpdatePlatformProfileReactor.java new file mode 100644 index 00000000000..f6247cb138d --- /dev/null +++ b/src/prerna/reactor/platformprofile/UpdatePlatformProfileReactor.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.platformprofile; + +import prerna.auth.User; +import prerna.auth.utils.PlatformProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class UpdatePlatformProfileReactor extends AbstractReactor { + + public UpdatePlatformProfileReactor() { + this.keysToGet = new String[] { "profileId", "name", "description" }; + this.keyRequired = new int[] { 1, 0, 0 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + if (!PlatformProfileUtils.canManage(user)) { + throw new IllegalArgumentException("User must be an admin to manage platform profiles."); + } + String profileId = this.keyValue.get("profileId"); + String name = this.keyValue.get("name"); + String description = this.keyValue.get("description"); + PlatformProfileUtils.updateProfile(profileId, name, description, user); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Platform profile updated.")); + return noun; + } + + @Override + public String getReactorDescription() { + return "Update a platform profile."; + } +} From 7fc44c2a250bc15fed1b97c8b059987f6ae27613 Mon Sep 17 00:00:00 2001 From: "Patel, Parth" Date: Wed, 24 Jun 2026 15:51:51 -0400 Subject: [PATCH 2/9] fix: replace SelectQueryStruct with PreparedStatement in AppProfileUtils getProfiles() and getFeatures() used SelectQueryStruct/WrapperManager which performs OWL metadata lookups. APP_PROFILE and APP_FEATURE are not registered in SEMOSS's schema, so getPhysicalPropertyNameFromConceptualName returns null causing NPE during SQL composition. Co-Authored-By: Claude Sonnet 4.6 --- src/prerna/auth/utils/AppProfileUtils.java | 74 +++++++++++----------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/src/prerna/auth/utils/AppProfileUtils.java b/src/prerna/auth/utils/AppProfileUtils.java index 6d6a748c1a3..ab2360f8eda 100644 --- a/src/prerna/auth/utils/AppProfileUtils.java +++ b/src/prerna/auth/utils/AppProfileUtils.java @@ -220,30 +220,30 @@ public static void deleteProfile(String appId, String profileId, User user) { public static List> getProfiles(String appId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); List> profiles = new ArrayList<>(); - SelectQueryStruct qs = new SelectQueryStruct(); - qs.addSelector(new QueryColumnSelector("APP_PROFILE__PROFILE_ID")); - qs.addSelector(new QueryColumnSelector("APP_PROFILE__PROFILE_NAME")); - qs.addSelector(new QueryColumnSelector("APP_PROFILE__DESCRIPTION")); - qs.addSelector(new QueryColumnSelector("APP_PROFILE__IS_DEFAULT")); - qs.addSelector(new QueryColumnSelector("APP_PROFILE__CREATED_BY")); - qs.addSelector(new QueryColumnSelector("APP_PROFILE__CREATED_AT")); - qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE__APP_ID", "==", appId)); - qs.addOrderBy("APP_PROFILE__PROFILE_NAME", "ASC"); - try (IRawSelectWrapper wrapper = WrapperManager.getInstance().getRawWrapper(securityDb, qs)) { - while (wrapper.hasNext()) { - Object[] row = wrapper.next().getValues(); + String sql = "SELECT PROFILE_ID, PROFILE_NAME, DESCRIPTION, IS_DEFAULT, CREATED_BY, CREATED_AT " + + "FROM APP_PROFILE WHERE APP_ID=? ORDER BY PROFILE_NAME ASC"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, appId); + rs = ps.executeQuery(); + while (rs.next()) { + String profileId = rs.getString("PROFILE_ID"); Map profile = new HashMap<>(); - profile.put("profileId", row[0]); - profile.put("profileName", row[1]); - profile.put("description", row[2]); - profile.put("isDefault", row[3]); - profile.put("createdBy", row[4]); - profile.put("createdAt", row[5]); - profile.put("userCount", getAssignedUserCount(securityDb, appId, (String) row[0])); + profile.put("profileId", profileId); + profile.put("profileName", rs.getString("PROFILE_NAME")); + profile.put("description", rs.getString("DESCRIPTION")); + profile.put("isDefault", rs.getBoolean("IS_DEFAULT")); + profile.put("createdBy", rs.getString("CREATED_BY")); + profile.put("createdAt", rs.getTimestamp("CREATED_AT")); + profile.put("userCount", getAssignedUserCount(securityDb, appId, profileId)); profiles.add(profile); } - } catch (Exception e) { + } catch (SQLException e) { classLogger.error("Failed to get app profiles", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); } return profiles; } @@ -342,27 +342,27 @@ public static void deleteFeature(String appId, String featureId, User user) { public static List> getFeatures(String appId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); List> features = new ArrayList<>(); - SelectQueryStruct qs = new SelectQueryStruct(); - qs.addSelector(new QueryColumnSelector("APP_FEATURE__FEATURE_ID")); - qs.addSelector(new QueryColumnSelector("APP_FEATURE__FEATURE_KEY")); - qs.addSelector(new QueryColumnSelector("APP_FEATURE__DESCRIPTION")); - qs.addSelector(new QueryColumnSelector("APP_FEATURE__CREATED_BY")); - qs.addSelector(new QueryColumnSelector("APP_FEATURE__CREATED_AT")); - qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_FEATURE__APP_ID", "==", appId)); - qs.addOrderBy("APP_FEATURE__FEATURE_KEY", "ASC"); - try (IRawSelectWrapper wrapper = WrapperManager.getInstance().getRawWrapper(securityDb, qs)) { - while (wrapper.hasNext()) { - Object[] row = wrapper.next().getValues(); + String sql = "SELECT FEATURE_ID, FEATURE_KEY, DESCRIPTION, CREATED_BY, CREATED_AT " + + "FROM APP_FEATURE WHERE APP_ID=? ORDER BY FEATURE_KEY ASC"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, appId); + rs = ps.executeQuery(); + while (rs.next()) { Map feature = new HashMap<>(); - feature.put("featureId", row[0]); - feature.put("featureKey", row[1]); - feature.put("description", row[2]); - feature.put("createdBy", row[3]); - feature.put("createdAt", row[4]); + feature.put("featureId", rs.getString("FEATURE_ID")); + feature.put("featureKey", rs.getString("FEATURE_KEY")); + feature.put("description", rs.getString("DESCRIPTION")); + feature.put("createdBy", rs.getString("CREATED_BY")); + feature.put("createdAt", rs.getTimestamp("CREATED_AT")); features.add(feature); } - } catch (Exception e) { + } catch (SQLException e) { classLogger.error("Failed to get app features", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); } return features; } From 159b8ae1381810f47ca81a7f9c0ba71e9dd48f32 Mon Sep 17 00:00:00 2001 From: "Patel, Parth" Date: Wed, 24 Jun 2026 16:09:03 -0400 Subject: [PATCH 3/9] fix: add GetPlatformProfileUsers reactor and utility method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add getPlatformProfileUsers() to PlatformProfileUtils — queries PLATFORM_USER_PROFILE for all users assigned to a given profile - Add GetPlatformProfileUsersReactor (admin-only) so the admin UI Members tab can list users assigned to each platform profile Co-Authored-By: Claude Sonnet 4.6 --- .../auth/utils/PlatformProfileUtils.java | 25 ++++++++ .../GetPlatformProfileUsersReactor.java | 62 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 src/prerna/reactor/platformprofile/GetPlatformProfileUsersReactor.java diff --git a/src/prerna/auth/utils/PlatformProfileUtils.java b/src/prerna/auth/utils/PlatformProfileUtils.java index 5a775374b43..6d941319a0f 100644 --- a/src/prerna/auth/utils/PlatformProfileUtils.java +++ b/src/prerna/auth/utils/PlatformProfileUtils.java @@ -333,6 +333,31 @@ public static Map getUserFeatures(User user) { return getProfileFeatures(profileId); } + public static List> getPlatformProfileUsers(String profileId) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + List> users = new ArrayList<>(); + String sql = "SELECT USER_ID, ASSIGNED_BY, ASSIGNED_AT FROM PLATFORM_USER_PROFILE WHERE PROFILE_ID=?"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, profileId); + rs = ps.executeQuery(); + while (rs.next()) { + Map row = new HashMap<>(); + row.put("userId", rs.getString("USER_ID")); + row.put("assignedBy", rs.getString("ASSIGNED_BY")); + row.put("assignedAt", rs.getTimestamp("ASSIGNED_AT")); + users.add(row); + } + } catch (SQLException e) { + classLogger.error("Failed to get platform profile users", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + return users; + } + // ─── Private helpers ───────────────────────────────────────────────────── private static int getAssignedUserCount(IRDBMSEngine securityDb, String profileId) { diff --git a/src/prerna/reactor/platformprofile/GetPlatformProfileUsersReactor.java b/src/prerna/reactor/platformprofile/GetPlatformProfileUsersReactor.java new file mode 100644 index 00000000000..01aab9eb4d8 --- /dev/null +++ b/src/prerna/reactor/platformprofile/GetPlatformProfileUsersReactor.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.platformprofile; + +import java.util.List; +import java.util.Map; + +import prerna.auth.User; +import prerna.auth.utils.PlatformProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class GetPlatformProfileUsersReactor extends AbstractReactor { + + public GetPlatformProfileUsersReactor() { + this.keysToGet = new String[] { "profileId" }; + this.keyRequired = new int[] { 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + if (!PlatformProfileUtils.canManage(user)) { + throw new IllegalArgumentException("User must be an admin to manage platform profiles."); + } + String profileId = this.keyValue.get("profileId"); + List> users = PlatformProfileUtils.getPlatformProfileUsers(profileId); + return new NounMetadata(users, PixelDataType.CUSTOM_DATA_STRUCTURE); + } + + @Override + public String getReactorDescription() { + return "Get all users assigned to a platform profile."; + } +} From 6b1b4b31388d86ed5ce314ff5653ae2fd8388760 Mon Sep 17 00:00:00 2001 From: "Patel, Parth" Date: Thu, 25 Jun 2026 13:57:49 -0400 Subject: [PATCH 4/9] feat: multi-profile, group profiles, delegated BU admin, and reactor code quality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Multi-profile: users can be assigned to multiple profiles simultaneously; GetAppUserFeatures returns union of features across all assignments - Group-style profiles (isGroup=true): named sub-groups with independent feature flags; users assigned to sub-groups via AssignAppUserSubgroup - Delegated BU admin: APP_PROFILE_MANAGER table grants 'assign' permission so non-owners can manage user assignments without full profile management rights - New DB tables: APP_PROFILE_SUBGROUP, APP_SUBGROUP_FEATURE, APP_USER_SUBGROUP, APP_PROFILE_MANAGER (via SecurityOwlCreator + AbstractSecurityUtils migration) - New Pixel commands: GetUserAppProfiles, GetAppUserFeatures, CheckAppFeature, AssignAppUserProfile, RemoveAppUserProfile, CreateAppSubgroup, UpdateAppSubgroup, DeleteAppSubgroup, GetAppSubgroups, SetAppSubgroupFeature, GetAppSubgroupFeatures, AssignAppUserSubgroup, RemoveAppUserSubgroup, AddAppProfileManager, GetAppProfileManagers, RemoveAppProfileManager, GetUserAppProfile - Renamed for domain clarity: GetProfileFeatures→GetAppProfileFeatures, SetProfileFeature→SetAppProfileFeature, SetSubgroupFeature→SetAppSubgroupFeature, GetSubgroupFeatures→GetAppSubgroupFeatures, GetSubgroupUsers→GetAppSubgroupUsers, GetProfileUsers→GetAppProfileUsers, AddProfileManager→AddAppProfileManager, GetProfileManagers→GetAppProfileManagers, RemoveProfileManager→RemoveAppProfileManager - Backwards-compat aliases kept with @Deprecated for old Pixel names - Code quality: classLogger on all reactors, ReactorKeysEnum constants for all key strings (9 new enum entries added), PixelOperationType on all NounMetadata returns, CUSTOM_DATA_STRUCTURE for list-of-maps (was VECTOR), N+1 queries eliminated in getProfiles() and getSubgroups() via SQL subqueries, canEvaluateFeatures admin bypass added - featureKey param renamed from "key" in CreateAppFeature/UpdateAppFeature Co-Authored-By: Claude Sonnet 4.6 --- .../auth/utils/AbstractSecurityUtils.java | 115 +- src/prerna/auth/utils/AppProfileUtils.java | 1105 ++++++++++++++--- src/prerna/auth/utils/SecurityOwlCreator.java | 103 ++ .../AddAppProfileManagerReactor.java | 70 ++ .../AssignAppUserProfileReactor.java | 70 ++ .../AssignAppUserSubgroupReactor.java | 70 ++ .../appprofile/AssignUserProfileReactor.java | 27 +- .../appprofile/CheckAppFeatureReactor.java | 67 + .../appprofile/CheckFeatureReactor.java | 18 +- .../appprofile/CreateAppFeatureReactor.java | 16 +- .../appprofile/CreateAppProfileReactor.java | 23 +- .../appprofile/CreateAppSubgroupReactor.java | 73 ++ .../appprofile/DeleteAppFeatureReactor.java | 14 +- .../appprofile/DeleteAppProfileReactor.java | 14 +- .../appprofile/DeleteAppSubgroupReactor.java | 69 + .../appprofile/GetAppFeaturesReactor.java | 12 +- ...java => GetAppProfileFeaturesReactor.java} | 19 +- .../GetAppProfileManagersReactor.java | 70 ++ ...or.java => GetAppProfileUsersReactor.java} | 19 +- .../appprofile/GetAppProfilesReactor.java | 12 +- .../GetAppSubgroupFeaturesReactor.java | 71 ++ .../GetAppSubgroupUsersReactor.java | 71 ++ .../appprofile/GetAppSubgroupsReactor.java | 70 ++ .../appprofile/GetAppUserFeaturesReactor.java | 68 + .../appprofile/GetUserAppProfileReactor.java | 72 ++ .../appprofile/GetUserAppProfilesReactor.java | 68 + .../appprofile/GetUserFeaturesReactor.java | 16 +- .../appprofile/GetUserProfileReactor.java | 19 +- .../RemoveAppProfileManagerReactor.java | 70 ++ .../RemoveAppUserProfileReactor.java | 70 ++ .../RemoveAppUserSubgroupReactor.java | 70 ++ .../appprofile/RemoveUserProfileReactor.java | 27 +- ....java => SetAppProfileFeatureReactor.java} | 23 +- .../SetAppSubgroupFeatureReactor.java | 72 ++ .../appprofile/UpdateAppFeatureReactor.java | 18 +- .../appprofile/UpdateAppProfileReactor.java | 26 +- .../appprofile/UpdateAppSubgroupReactor.java | 71 ++ src/prerna/sablecc2/om/ReactorKeysEnum.java | 16 +- 38 files changed, 2666 insertions(+), 238 deletions(-) create mode 100644 src/prerna/reactor/appprofile/AddAppProfileManagerReactor.java create mode 100644 src/prerna/reactor/appprofile/AssignAppUserProfileReactor.java create mode 100644 src/prerna/reactor/appprofile/AssignAppUserSubgroupReactor.java create mode 100644 src/prerna/reactor/appprofile/CheckAppFeatureReactor.java create mode 100644 src/prerna/reactor/appprofile/CreateAppSubgroupReactor.java create mode 100644 src/prerna/reactor/appprofile/DeleteAppSubgroupReactor.java rename src/prerna/reactor/appprofile/{GetProfileFeaturesReactor.java => GetAppProfileFeaturesReactor.java} (78%) create mode 100644 src/prerna/reactor/appprofile/GetAppProfileManagersReactor.java rename src/prerna/reactor/appprofile/{GetProfileUsersReactor.java => GetAppProfileUsersReactor.java} (78%) create mode 100644 src/prerna/reactor/appprofile/GetAppSubgroupFeaturesReactor.java create mode 100644 src/prerna/reactor/appprofile/GetAppSubgroupUsersReactor.java create mode 100644 src/prerna/reactor/appprofile/GetAppSubgroupsReactor.java create mode 100644 src/prerna/reactor/appprofile/GetAppUserFeaturesReactor.java create mode 100644 src/prerna/reactor/appprofile/GetUserAppProfileReactor.java create mode 100644 src/prerna/reactor/appprofile/GetUserAppProfilesReactor.java create mode 100644 src/prerna/reactor/appprofile/RemoveAppProfileManagerReactor.java create mode 100644 src/prerna/reactor/appprofile/RemoveAppUserProfileReactor.java create mode 100644 src/prerna/reactor/appprofile/RemoveAppUserSubgroupReactor.java rename src/prerna/reactor/appprofile/{SetProfileFeatureReactor.java => SetAppProfileFeatureReactor.java} (72%) create mode 100644 src/prerna/reactor/appprofile/SetAppSubgroupFeatureReactor.java create mode 100644 src/prerna/reactor/appprofile/UpdateAppSubgroupReactor.java diff --git a/src/prerna/auth/utils/AbstractSecurityUtils.java b/src/prerna/auth/utils/AbstractSecurityUtils.java index c83eab7b031..05b7dc887cb 100644 --- a/src/prerna/auth/utils/AbstractSecurityUtils.java +++ b/src/prerna/auth/utils/AbstractSecurityUtils.java @@ -2415,9 +2415,9 @@ public static void initialize() throws Exception { } // APP_PROFILE — named profiles per app - colNames = new String[] { "PROFILE_ID", "APP_ID", "PROFILE_NAME", "DESCRIPTION", "IS_DEFAULT", "CREATED_BY", "CREATED_AT" }; - types = new String[] { "VARCHAR(255)", "VARCHAR(255)", "VARCHAR(255)", "VARCHAR(2000)", BOOLEAN_DATATYPE_NAME, "VARCHAR(255)", TIMESTAMP_DATATYPE_NAME }; - defaultValues = new Object[] { null, null, null, null, false, null, null }; + colNames = new String[] { "PROFILE_ID", "APP_ID", "PROFILE_NAME", "DESCRIPTION", "IS_DEFAULT", "IS_GROUP", "CREATED_BY", "CREATED_AT" }; + types = new String[] { "VARCHAR(255)", "VARCHAR(255)", "VARCHAR(255)", "VARCHAR(2000)", BOOLEAN_DATATYPE_NAME, BOOLEAN_DATATYPE_NAME, "VARCHAR(255)", TIMESTAMP_DATATYPE_NAME }; + defaultValues = new Object[] { null, null, null, null, false, false, null, null }; if (allowIfExistsTable) { String sql = queryUtil.createTableIfNotExistsWithDefaults("APP_PROFILE", colNames, types, defaultValues); classLogger.info("Running sql {}", sql); @@ -2606,6 +2606,115 @@ public static void initialize() throws Exception { } } + // APP_PROFILE_SUBGROUP — named sub-groups within a group-style profile + colNames = new String[] { "SUBGROUP_ID", "PROFILE_ID", "APP_ID", "SUBGROUP_NAME", "DESCRIPTION", "CREATED_BY", "CREATED_AT" }; + types = new String[] { "VARCHAR(255)", "VARCHAR(255)", "VARCHAR(255)", "VARCHAR(255)", "VARCHAR(2000)", "VARCHAR(255)", TIMESTAMP_DATATYPE_NAME }; + if (allowIfExistsTable) { + String sql = queryUtil.createTableIfNotExists("APP_PROFILE_SUBGROUP", colNames, types); + classLogger.info("Running sql {}", sql); + securityDb.insertData(sql); + } else { + if (!queryUtil.tableExists(conn, "APP_PROFILE_SUBGROUP", database, schema)) { + String sql = queryUtil.createTable("APP_PROFILE_SUBGROUP", colNames, types); + classLogger.info("Running sql {}", sql); + securityDb.insertData(sql); + } + } + { + List allCols = queryUtil.getTableColumns(conn, "APP_PROFILE_SUBGROUP", database, schema); + for (int i = 0; i < colNames.length; i++) { + String col = colNames[i]; + if (!allCols.contains(col) && !allCols.contains(col.toLowerCase())) { + classLogger.info("Column '{}' is not present in current list of columns: {}", col, allCols); + String addColumnSql = queryUtil.alterTableAddColumn("APP_PROFILE_SUBGROUP", col, types[i]); + classLogger.info("Running sql {}", addColumnSql); + securityDb.insertData(addColumnSql); + } + } + } + + // APP_SUBGROUP_FEATURE — feature flags per sub-group + colNames = new String[] { "APP_ID", "SUBGROUP_ID", "FEATURE_ID", "ENABLED" }; + types = new String[] { "VARCHAR(255)", "VARCHAR(255)", "VARCHAR(255)", BOOLEAN_DATATYPE_NAME }; + defaultValues = new Object[] { null, null, null, true }; + if (allowIfExistsTable) { + String sql = queryUtil.createTableIfNotExistsWithDefaults("APP_SUBGROUP_FEATURE", colNames, types, defaultValues); + classLogger.info("Running sql {}", sql); + securityDb.insertData(sql); + } else { + if (!queryUtil.tableExists(conn, "APP_SUBGROUP_FEATURE", database, schema)) { + String sql = queryUtil.createTable("APP_SUBGROUP_FEATURE", colNames, types); + classLogger.info("Running sql {}", sql); + securityDb.insertData(sql); + } + } + { + List allCols = queryUtil.getTableColumns(conn, "APP_SUBGROUP_FEATURE", database, schema); + for (int i = 0; i < colNames.length; i++) { + String col = colNames[i]; + if (!allCols.contains(col) && !allCols.contains(col.toLowerCase())) { + classLogger.info("Column '{}' is not present in current list of columns: {}", col, allCols); + String addColumnSql = queryUtil.alterTableAddColumn("APP_SUBGROUP_FEATURE", col, types[i]); + classLogger.info("Running sql {}", addColumnSql); + securityDb.insertData(addColumnSql); + } + } + } + + // APP_USER_SUBGROUP — user-to-subgroup assignment (many-to-many) + colNames = new String[] { "APP_ID", "USER_ID", "SUBGROUP_ID", "ASSIGNED_BY", "ASSIGNED_AT" }; + types = new String[] { "VARCHAR(255)", "VARCHAR(255)", "VARCHAR(255)", "VARCHAR(255)", TIMESTAMP_DATATYPE_NAME }; + if (allowIfExistsTable) { + String sql = queryUtil.createTableIfNotExists("APP_USER_SUBGROUP", colNames, types); + classLogger.info("Running sql {}", sql); + securityDb.insertData(sql); + } else { + if (!queryUtil.tableExists(conn, "APP_USER_SUBGROUP", database, schema)) { + String sql = queryUtil.createTable("APP_USER_SUBGROUP", colNames, types); + classLogger.info("Running sql {}", sql); + securityDb.insertData(sql); + } + } + { + List allCols = queryUtil.getTableColumns(conn, "APP_USER_SUBGROUP", database, schema); + for (int i = 0; i < colNames.length; i++) { + String col = colNames[i]; + if (!allCols.contains(col) && !allCols.contains(col.toLowerCase())) { + classLogger.info("Column '{}' is not present in current list of columns: {}", col, allCols); + String addColumnSql = queryUtil.alterTableAddColumn("APP_USER_SUBGROUP", col, types[i]); + classLogger.info("Running sql {}", addColumnSql); + securityDb.insertData(addColumnSql); + } + } + } + + // APP_PROFILE_MANAGER — delegated assign permission for BU admins + colNames = new String[] { "APP_ID", "USER_ID", "PERMISSION" }; + types = new String[] { "VARCHAR(255)", "VARCHAR(255)", "VARCHAR(50)" }; + if (allowIfExistsTable) { + String sql = queryUtil.createTableIfNotExists("APP_PROFILE_MANAGER", colNames, types); + classLogger.info("Running sql {}", sql); + securityDb.insertData(sql); + } else { + if (!queryUtil.tableExists(conn, "APP_PROFILE_MANAGER", database, schema)) { + String sql = queryUtil.createTable("APP_PROFILE_MANAGER", colNames, types); + classLogger.info("Running sql {}", sql); + securityDb.insertData(sql); + } + } + { + List allCols = queryUtil.getTableColumns(conn, "APP_PROFILE_MANAGER", database, schema); + for (int i = 0; i < colNames.length; i++) { + String col = colNames[i]; + if (!allCols.contains(col) && !allCols.contains(col.toLowerCase())) { + classLogger.info("Column '{}' is not present in current list of columns: {}", col, allCols); + String addColumnSql = queryUtil.alterTableAddColumn("APP_PROFILE_MANAGER", col, types[i]); + classLogger.info("Running sql {}", addColumnSql); + securityDb.insertData(addColumnSql); + } + } + } + if (!conn.getAutoCommit()) { conn.commit(); } diff --git a/src/prerna/auth/utils/AppProfileUtils.java b/src/prerna/auth/utils/AppProfileUtils.java index ab2360f8eda..c3a19e763a3 100644 --- a/src/prerna/auth/utils/AppProfileUtils.java +++ b/src/prerna/auth/utils/AppProfileUtils.java @@ -32,6 +32,7 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.UUID; @@ -63,24 +64,52 @@ private AppProfileUtils() { // ─── Permission checks ────────────────────────────────────────────────── public static boolean canManageProfiles(User user, String appId) { + if (SecurityAdminUtils.userIsAdmin(user)) return true; if (!appExists(appId)) { throw new IllegalArgumentException("App not found: " + appId); } - if (SecurityAdminUtils.userIsAdmin(user)) return true; if (SecurityProjectUtils.userIsOwner(user, appId)) return true; if (SecurityProjectUtils.userCanEditProject(user, appId)) return true; return false; } + /** + * Returns true if the user can assign/remove users from profiles (either as a + * full manager or as a delegated BU admin with 'assign' permission). + */ + public static boolean canAssignProfiles(User user, String appId) { + if (canManageProfiles(user, appId)) return true; + String userId = getUserId(user); + if (userId == null) return false; + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + String sql = "SELECT COUNT(*) FROM APP_PROFILE_MANAGER WHERE APP_ID=? AND USER_ID=? AND PERMISSION='assign'"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, appId); + ps.setString(2, userId); + rs = ps.executeQuery(); + if (rs.next()) return rs.getInt(1) > 0; + } catch (SQLException e) { + classLogger.error("Failed to check assign permission", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + return false; + } + public static boolean canEvaluateFeatures(User user, String appId) { + if (SecurityAdminUtils.userIsAdmin(user)) return true; + if (canAssignProfiles(user, appId)) return true; return SecurityProjectUtils.userCanViewProject(user, appId); } private static boolean appExists(String appId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); SelectQueryStruct qs = new SelectQueryStruct(); - qs.addSelector(new QueryColumnSelector("PROJECTPERMISSION__PROJECTID")); - qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("PROJECTPERMISSION__PROJECTID", "==", appId)); + qs.addSelector(new QueryColumnSelector("PROJECT__PROJECTID")); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("PROJECT__PROJECTID", "==", appId)); try (IRawSelectWrapper wrapper = WrapperManager.getInstance().getRawWrapper(securityDb, qs)) { return wrapper.hasNext(); } catch (Exception e) { @@ -92,7 +121,7 @@ private static boolean appExists(String appId) { // ─── Profile CRUD ─────────────────────────────────────────────────────── public static Map createProfile(String appId, String name, String description, - boolean isDefault, User user) { + boolean isDefault, boolean isGroup, User user) { if (name == null || name.trim().isEmpty()) { throw new IllegalArgumentException("Profile name cannot be blank."); } @@ -111,13 +140,14 @@ public static Map createProfile(String appId, String name, Strin PreparedStatement ps = null; try { ps = securityDb.getPreparedStatement( - "INSERT INTO APP_PROFILE (PROFILE_ID, APP_ID, PROFILE_NAME, DESCRIPTION, IS_DEFAULT, CREATED_BY, CREATED_AT) VALUES (?,?,?,?,?,?,?)"); + "INSERT INTO APP_PROFILE (PROFILE_ID, APP_ID, PROFILE_NAME, DESCRIPTION, IS_DEFAULT, IS_GROUP, CREATED_BY, CREATED_AT) VALUES (?,?,?,?,?,?,?,?)"); int i = 1; ps.setString(i++, profileId); ps.setString(i++, appId); ps.setString(i++, profileName); ps.setString(i++, description); ps.setBoolean(i++, isDefault); + ps.setBoolean(i++, isGroup); ps.setString(i++, actorId); ps.setTimestamp(i++, Utility.getCurrentSqlTimestampUTC()); ps.execute(); @@ -136,11 +166,12 @@ public static Map createProfile(String appId, String name, Strin result.put("profileName", profileName); result.put("description", description); result.put("isDefault", isDefault); + result.put("isGroup", isGroup); return result; } public static void updateProfile(String appId, String profileId, String name, String description, - Boolean isDefault, User user) { + Boolean isDefault, Boolean isGroup, User user) { if (name != null) { if (name.trim().isEmpty()) throw new IllegalArgumentException("Profile name cannot be blank."); if (name.trim().length() > 100) throw new IllegalArgumentException("Profile name cannot exceed 100 characters."); @@ -154,6 +185,7 @@ public static void updateProfile(String appId, String profileId, String name, St if (name != null) { sb.append(" PROFILE_NAME=?,"); params.add(name.trim()); } if (description != null) { sb.append(" DESCRIPTION=?,"); params.add(description); } if (isDefault != null) { sb.append(" IS_DEFAULT=?,"); params.add(isDefault); } + if (isGroup != null) { sb.append(" IS_GROUP=?,"); params.add(isGroup); } if (params.isEmpty()) return; sb.setLength(sb.length() - 1); sb.append(" WHERE PROFILE_ID=? AND APP_ID=?"); @@ -215,13 +247,31 @@ public static void deleteProfile(String appId, String profileId, User user) { } finally { ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); } + // cascade delete subgroups + List subgroupIds = getSubgroupIdsForProfile(securityDb, appId, profileId); + for (String subgroupId : subgroupIds) { + deleteSubgroupInternal(securityDb, appId, subgroupId); + } + ps = null; + try { + ps = securityDb.getPreparedStatement("DELETE FROM APP_PROFILE_SUBGROUP WHERE PROFILE_ID=? AND APP_ID=?"); + ps.setString(1, profileId); + ps.setString(2, appId); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to cascade delete subgroups for profile", e); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } } public static List> getProfiles(String appId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); List> profiles = new ArrayList<>(); - String sql = "SELECT PROFILE_ID, PROFILE_NAME, DESCRIPTION, IS_DEFAULT, CREATED_BY, CREATED_AT " - + "FROM APP_PROFILE WHERE APP_ID=? ORDER BY PROFILE_NAME ASC"; + String sql = "SELECT p.PROFILE_ID, p.PROFILE_NAME, p.DESCRIPTION, p.IS_DEFAULT, p.IS_GROUP, p.CREATED_BY, p.CREATED_AT, " + + "(SELECT COUNT(DISTINCT up.USER_ID) FROM APP_USER_PROFILE up WHERE up.APP_ID=p.APP_ID AND up.PROFILE_ID=p.PROFILE_ID) AS USER_COUNT " + + "FROM APP_PROFILE p WHERE p.APP_ID=? ORDER BY p.PROFILE_NAME ASC"; PreparedStatement ps = null; ResultSet rs = null; try { @@ -229,15 +279,15 @@ public static List> getProfiles(String appId) { ps.setString(1, appId); rs = ps.executeQuery(); while (rs.next()) { - String profileId = rs.getString("PROFILE_ID"); Map profile = new HashMap<>(); - profile.put("profileId", profileId); + profile.put("profileId", rs.getString("PROFILE_ID")); profile.put("profileName", rs.getString("PROFILE_NAME")); profile.put("description", rs.getString("DESCRIPTION")); profile.put("isDefault", rs.getBoolean("IS_DEFAULT")); + profile.put("isGroup", rs.getBoolean("IS_GROUP")); profile.put("createdBy", rs.getString("CREATED_BY")); profile.put("createdAt", rs.getTimestamp("CREATED_AT")); - profile.put("userCount", getAssignedUserCount(securityDb, appId, profileId)); + profile.put("userCount", rs.getInt("USER_COUNT")); profiles.add(profile); } } catch (SQLException e) { @@ -337,6 +387,18 @@ public static void deleteFeature(String appId, String featureId, User user) { } finally { ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); } + ps = null; + try { + ps = securityDb.getPreparedStatement("DELETE FROM APP_SUBGROUP_FEATURE WHERE FEATURE_ID=? AND APP_ID=?"); + ps.setString(1, featureId); + ps.setString(2, appId); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to cascade delete feature from subgroup mappings", e); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } } public static List> getFeatures(String appId) { @@ -407,25 +469,40 @@ public static void setProfileFeature(String appId, String profileId, String feat public static List> getProfileFeatures(String appId, String profileId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); List> results = new ArrayList<>(); - String sql = "SELECT f.FEATURE_ID, f.FEATURE_KEY, f.DESCRIPTION, pf.ENABLED " - + "FROM APP_FEATURE f " - + "LEFT JOIN APP_PROFILE_FEATURE pf ON f.FEATURE_ID = pf.FEATURE_ID " - + "AND f.APP_ID = pf.APP_ID AND pf.PROFILE_ID = ? " - + "WHERE f.APP_ID = ? ORDER BY f.FEATURE_KEY"; + Map enabledMap = new HashMap<>(); + String assignSql = "SELECT FEATURE_ID, ENABLED FROM APP_PROFILE_FEATURE " + + "WHERE APP_ID=? AND PROFILE_ID=?"; PreparedStatement ps = null; ResultSet rs = null; try { - ps = securityDb.getPreparedStatement(sql); - ps.setString(1, profileId); - ps.setString(2, appId); + ps = securityDb.getPreparedStatement(assignSql); + ps.setString(1, appId); + ps.setString(2, profileId); + rs = ps.executeQuery(); + while (rs.next()) { + enabledMap.put(rs.getString("FEATURE_ID"), rs.getBoolean("ENABLED")); + } + } catch (SQLException e) { + classLogger.error("Failed to get profile feature assignments", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + + String featSql = "SELECT FEATURE_ID, FEATURE_KEY, DESCRIPTION " + + "FROM APP_FEATURE WHERE APP_ID=? ORDER BY FEATURE_KEY ASC"; + ps = null; + rs = null; + try { + ps = securityDb.getPreparedStatement(featSql); + ps.setString(1, appId); rs = ps.executeQuery(); while (rs.next()) { + String featureId = rs.getString("FEATURE_ID"); Map row = new HashMap<>(); - row.put("featureId", rs.getString("FEATURE_ID")); + row.put("featureId", featureId); row.put("featureKey", rs.getString("FEATURE_KEY")); row.put("description", rs.getString("DESCRIPTION")); - Object enabledObj = rs.getObject("ENABLED"); - row.put("enabled", enabledObj != null && rs.getBoolean("ENABLED")); + row.put("enabled", enabledMap.getOrDefault(featureId, Boolean.FALSE)); results.add(row); } } catch (SQLException e) { @@ -436,23 +513,34 @@ public static List> getProfileFeatures(String appId, String return results; } - // ─── User-Profile Assignment ──────────────────────────────────────────── + // ─── User-Profile Assignment (multi-profile) ──────────────────────────── + /** + * Assigns a user to a profile. A user can be in multiple profiles simultaneously. + * If already assigned to this specific profile, this is a no-op. + */ public static void assignUserProfile(String appId, String userId, String profileId, User actor) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - String actorId = getUserId(actor); + // check for existing assignment to avoid duplicates + String checkSql = "SELECT COUNT(*) FROM APP_USER_PROFILE WHERE APP_ID=? AND USER_ID=? AND PROFILE_ID=?"; PreparedStatement ps = null; + ResultSet rs = null; try { - ps = securityDb.getPreparedStatement("DELETE FROM APP_USER_PROFILE WHERE APP_ID=? AND USER_ID=?"); + ps = securityDb.getPreparedStatement(checkSql); ps.setString(1, appId); ps.setString(2, userId); - ps.execute(); - if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + ps.setString(3, profileId); + rs = ps.executeQuery(); + if (rs.next() && rs.getInt(1) > 0) { + return; // already assigned + } } catch (SQLException e) { - classLogger.error("Failed to remove existing user profile assignment", e); + classLogger.error("Failed to check existing profile assignment", e); } finally { - ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + ConnectionUtils.closeAllConnections(ps, rs); } + + String actorId = getUserId(actor); ps = null; try { ps = securityDb.getPreparedStatement( @@ -472,6 +560,9 @@ public static void assignUserProfile(String appId, String userId, String profile } } + /** + * Removes ALL profile assignments for a user from an app (used when removing user from app entirely). + */ public static void removeUserProfile(String appId, String userId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); PreparedStatement ps = null; @@ -482,45 +573,114 @@ public static void removeUserProfile(String appId, String userId) { ps.execute(); if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); } catch (SQLException e) { - classLogger.error("Failed to remove user profile assignment", e); + classLogger.error("Failed to remove all user profile assignments", e); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + // Also remove all subgroup assignments + ps = null; + try { + ps = securityDb.getPreparedStatement("DELETE FROM APP_USER_SUBGROUP WHERE APP_ID=? AND USER_ID=?"); + ps.setString(1, appId); + ps.setString(2, userId); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to remove user subgroup assignments", e); } finally { ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); } } - public static Map getUserProfile(String appId, String userId) { + /** + * Removes a user from a specific profile assignment. + */ + public static void removeUserProfile(String appId, String userId, String profileId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - String sql = "SELECT p.PROFILE_ID, p.PROFILE_NAME FROM APP_USER_PROFILE up " - + "JOIN APP_PROFILE p ON up.PROFILE_ID = p.PROFILE_ID " - + "WHERE up.APP_ID = ? AND up.USER_ID = ?"; PreparedStatement ps = null; - ResultSet rs = null; try { - ps = securityDb.getPreparedStatement(sql); + ps = securityDb.getPreparedStatement( + "DELETE FROM APP_USER_PROFILE WHERE APP_ID=? AND USER_ID=? AND PROFILE_ID=?"); ps.setString(1, appId); ps.setString(2, userId); - rs = ps.executeQuery(); - if (rs.next()) { - Map result = new HashMap<>(); - result.put("profileId", rs.getString("PROFILE_ID")); - result.put("profileName", rs.getString("PROFILE_NAME")); - result.put("isExplicitAssignment", true); - return result; - } + ps.setString(3, profileId); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); } catch (SQLException e) { - classLogger.error("Failed to get user profile", e); + classLogger.error("Failed to remove user profile assignment", e); } finally { - ConnectionUtils.closeAllConnections(ps, rs); + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); } - return getDefaultProfile(securityDb, appId); + } + + /** + * Returns all explicit profile assignments for a user in an app. + * Each entry includes profileId, profileName, isGroup. + */ + public static List> getUserProfiles(String appId, String userId) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + return getExplicitUserProfiles(securityDb, appId, userId); + } + + /** + * Returns a structured summary of the calling user's profile memberships: + * - "profiles": list of directly-assigned standard profiles + * - "groups": map of parent profile name -> list of subgroup names the user is in + */ + public static Map getUserAppProfiles(String appId, User user) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + String userId = getUserId(user); + + Map result = new LinkedHashMap<>(); + List> directProfiles = new ArrayList<>(); + Map> groupMemberships = new LinkedHashMap<>(); + + // Standard profile assignments + List> explicitProfiles = getExplicitUserProfiles(securityDb, appId, userId); + boolean hasExplicit = !explicitProfiles.isEmpty(); + + for (Map p : explicitProfiles) { + boolean isGroup = (Boolean) p.getOrDefault("isGroup", Boolean.FALSE); + if (!isGroup) { + Map entry = new LinkedHashMap<>(); + entry.put("profileId", p.get("profileId")); + entry.put("profileName", p.get("profileName")); + entry.put("isDefault", false); + directProfiles.add(entry); + } + } + + // Fall back to default standard profile if no explicit assignment + if (!hasExplicit) { + Map defaultProfile = getDefaultProfile(securityDb, appId); + if (defaultProfile != null && !(Boolean) defaultProfile.getOrDefault("isGroup", Boolean.FALSE)) { + Map entry = new LinkedHashMap<>(); + entry.put("profileId", defaultProfile.get("profileId")); + entry.put("profileName", defaultProfile.get("profileName")); + entry.put("isDefault", true); + directProfiles.add(entry); + } + } + + // Subgroup memberships + List> subgroups = getExplicitUserSubgroups(securityDb, appId, userId); + for (Map sg : subgroups) { + String parentProfileName = (String) sg.get("profileName"); + String subgroupName = (String) sg.get("subgroupName"); + groupMemberships.computeIfAbsent(parentProfileName, k -> new ArrayList<>()).add(subgroupName); + } + + result.put("profiles", directProfiles); + result.put("groups", groupMemberships); + return result; } public static List> getProfileUsers(String appId, String profileId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); List> users = new ArrayList<>(); - String sql = "SELECT up.USER_ID, up.ASSIGNED_BY, up.ASSIGNED_AT " + String sql = "SELECT up.USER_ID, u.NAME, u.EMAIL, up.ASSIGNED_BY, up.ASSIGNED_AT " + "FROM APP_USER_PROFILE up " - + "INNER JOIN PROJECTPERMISSION pp ON up.USER_ID = pp.USERID AND up.APP_ID = pp.PROJECTID " + + "LEFT JOIN SMSS_USER u ON up.USER_ID = u.ID " + "WHERE up.APP_ID = ? AND up.PROFILE_ID = ?"; PreparedStatement ps = null; ResultSet rs = null; @@ -532,6 +692,8 @@ public static List> getProfileUsers(String appId, String pro while (rs.next()) { Map row = new HashMap<>(); row.put("userId", rs.getString("USER_ID")); + row.put("name", rs.getString("NAME")); + row.put("email", rs.getString("EMAIL")); row.put("assignedBy", rs.getString("ASSIGNED_BY")); row.put("assignedAt", rs.getTimestamp("ASSIGNED_AT")); users.add(row); @@ -544,87 +706,106 @@ public static List> getProfileUsers(String appId, String pro return users; } - // ─── Feature evaluation ────────────────────────────────────────────────── - - public static boolean checkFeature(String appId, String featureKey, User user) { - IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - String featureId = resolveFeatureId(securityDb, appId, featureKey); - if (featureId == null) return false; - String userId = getUserId(user); - Map profile = getUserProfile(appId, userId); - if (profile == null) return false; - String profileId = (String) profile.get("profileId"); - return queryFeatureEnabled(securityDb, appId, profileId, featureId); - } + // ─── Subgroup CRUD ─────────────────────────────────────────────────────── - public static Map getUserFeatures(String appId, User user) { + public static Map createSubgroup(String appId, String profileId, String name, + String description, User user) { + if (name == null || name.trim().isEmpty()) { + throw new IllegalArgumentException("Subgroup name cannot be blank."); + } + if (name.trim().length() > 100) { + throw new IllegalArgumentException("Subgroup name cannot exceed 100 characters."); + } + // Verify parent profile is group-style + if (!isGroupProfile(appId, profileId)) { + throw new IllegalArgumentException("Sub-groups can only be added to group-style profiles."); + } + String subgroupId = UUID.randomUUID().toString(); + String actorId = getUserId(user); IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - String userId = getUserId(user); - Map profile = getUserProfile(appId, userId); - if (profile == null) return new HashMap<>(); - String profileId = (String) profile.get("profileId"); - String profileName = (String) profile.get("profileName"); - boolean isDefaultProfile = !(Boolean) profile.getOrDefault("isExplicitAssignment", Boolean.TRUE); - - Map result = new HashMap<>(); - // Return only features with ENABLED=true — callers cannot infer what features exist but are hidden - String sql = "SELECT f.FEATURE_KEY, f.FEATURE_ID " - + "FROM APP_PROFILE_FEATURE pf " - + "JOIN APP_FEATURE f ON pf.FEATURE_ID = f.FEATURE_ID AND pf.APP_ID = f.APP_ID " - + "WHERE pf.APP_ID = ? AND pf.PROFILE_ID = ? AND pf.ENABLED = true"; PreparedStatement ps = null; - ResultSet rs = null; try { - ps = securityDb.getPreparedStatement(sql); - ps.setString(1, appId); - ps.setString(2, profileId); - rs = ps.executeQuery(); - while (rs.next()) { - Map featureInfo = new HashMap<>(); - featureInfo.put("featureId", rs.getString("FEATURE_ID")); - featureInfo.put("profileName", profileName); - featureInfo.put("isDefaultProfile", isDefaultProfile); - result.put(rs.getString("FEATURE_KEY"), featureInfo); - } + ps = securityDb.getPreparedStatement( + "INSERT INTO APP_PROFILE_SUBGROUP (SUBGROUP_ID, PROFILE_ID, APP_ID, SUBGROUP_NAME, DESCRIPTION, CREATED_BY, CREATED_AT) VALUES (?,?,?,?,?,?,?)"); + int i = 1; + ps.setString(i++, subgroupId); + ps.setString(i++, profileId); + ps.setString(i++, appId); + ps.setString(i++, name.trim()); + ps.setString(i++, description); + ps.setString(i++, actorId); + ps.setTimestamp(i++, Utility.getCurrentSqlTimestampUTC()); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); } catch (SQLException e) { - classLogger.error("Failed to get user features", e); + classLogger.error("Failed to create subgroup", e); + throw new IllegalArgumentException("An error occurred creating the subgroup."); } finally { - ConnectionUtils.closeAllConnections(ps, rs); + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); } + Map result = new HashMap<>(); + result.put("subgroupId", subgroupId); + result.put("profileId", profileId); + result.put("subgroupName", name.trim()); + result.put("description", description); return result; } - // ─── Private helpers ───────────────────────────────────────────────────── - - private static void validateFeatureKey(String key) { - if (key == null || key.isEmpty()) { - throw new IllegalArgumentException("Feature key cannot be blank."); - } - if (key.length() > FEATURE_KEY_MAX_LENGTH) { - throw new IllegalArgumentException("Feature key cannot exceed " + FEATURE_KEY_MAX_LENGTH + " characters."); + public static void updateSubgroup(String appId, String subgroupId, String name, + String description, User user) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + StringBuilder sb = new StringBuilder("UPDATE APP_PROFILE_SUBGROUP SET"); + List params = new ArrayList<>(); + if (name != null) { + if (name.trim().isEmpty()) throw new IllegalArgumentException("Subgroup name cannot be blank."); + sb.append(" SUBGROUP_NAME=?,"); params.add(name.trim()); } - if (!key.matches(FEATURE_KEY_PATTERN)) { - throw new IllegalArgumentException( - "Feature key must contain only alphanumeric characters and hyphens."); + if (description != null) { sb.append(" DESCRIPTION=?,"); params.add(description); } + if (params.isEmpty()) return; + sb.setLength(sb.length() - 1); + sb.append(" WHERE SUBGROUP_ID=? AND APP_ID=?"); + params.add(subgroupId); + params.add(appId); + PreparedStatement ps = null; + try { + ps = securityDb.getPreparedStatement(sb.toString()); + for (int i = 0; i < params.size(); i++) { + ps.setString(i + 1, params.get(i)); + } + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to update subgroup", e); + throw new IllegalArgumentException("An error occurred updating the subgroup."); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); } } - private static void clearDefaultProfile(IRDBMSEngine securityDb, String appId) { + public static void deleteSubgroup(String appId, String subgroupId, User user) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + deleteSubgroupInternal(securityDb, appId, subgroupId); PreparedStatement ps = null; try { - ps = securityDb.getPreparedStatement("UPDATE APP_PROFILE SET IS_DEFAULT=false WHERE APP_ID=?"); - ps.setString(1, appId); + ps = securityDb.getPreparedStatement("DELETE FROM APP_PROFILE_SUBGROUP WHERE SUBGROUP_ID=? AND APP_ID=?"); + ps.setString(1, subgroupId); + ps.setString(2, appId); ps.execute(); if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); } catch (SQLException e) { - classLogger.error("Failed to clear default profile", e); + classLogger.error("Failed to delete subgroup", e); + throw new IllegalArgumentException("An error occurred deleting the subgroup."); } finally { ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); } } - private static int getAssignedUserCount(IRDBMSEngine securityDb, String appId, String profileId) { - String sql = "SELECT COUNT(*) FROM APP_USER_PROFILE WHERE APP_ID=? AND PROFILE_ID=?"; + public static List> getSubgroups(String appId, String profileId) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + List> result = new ArrayList<>(); + String sql = "SELECT s.SUBGROUP_ID, s.SUBGROUP_NAME, s.DESCRIPTION, s.CREATED_BY, s.CREATED_AT, " + + "(SELECT COUNT(*) FROM APP_USER_SUBGROUP us WHERE us.APP_ID=s.APP_ID AND us.SUBGROUP_ID=s.SUBGROUP_ID) AS USER_COUNT " + + "FROM APP_PROFILE_SUBGROUP s WHERE s.APP_ID=? AND s.PROFILE_ID=? ORDER BY s.SUBGROUP_NAME ASC"; PreparedStatement ps = null; ResultSet rs = null; try { @@ -632,76 +813,718 @@ private static int getAssignedUserCount(IRDBMSEngine securityDb, String appId, S ps.setString(1, appId); ps.setString(2, profileId); rs = ps.executeQuery(); - if (rs.next()) return rs.getInt(1); + while (rs.next()) { + Map row = new HashMap<>(); + row.put("subgroupId", rs.getString("SUBGROUP_ID")); + row.put("subgroupName", rs.getString("SUBGROUP_NAME")); + row.put("description", rs.getString("DESCRIPTION")); + row.put("createdBy", rs.getString("CREATED_BY")); + row.put("createdAt", rs.getTimestamp("CREATED_AT")); + row.put("userCount", rs.getInt("USER_COUNT")); + result.add(row); + } } catch (SQLException e) { - classLogger.error("Failed to count profile users", e); + classLogger.error("Failed to get subgroups", e); } finally { ConnectionUtils.closeAllConnections(ps, rs); } - return 0; + return result; } - private static Map getDefaultProfile(IRDBMSEngine securityDb, String appId) { - String sql = "SELECT PROFILE_ID, PROFILE_NAME FROM APP_PROFILE WHERE APP_ID=? AND IS_DEFAULT=true"; + public static List> getSubgroupUsers(String appId, String subgroupId) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + List> users = new ArrayList<>(); + String sql = "SELECT us.USER_ID, u.NAME, u.EMAIL, us.ASSIGNED_BY, us.ASSIGNED_AT " + + "FROM APP_USER_SUBGROUP us " + + "LEFT JOIN SMSS_USER u ON us.USER_ID = u.ID " + + "WHERE us.APP_ID=? AND us.SUBGROUP_ID=?"; PreparedStatement ps = null; ResultSet rs = null; try { ps = securityDb.getPreparedStatement(sql); ps.setString(1, appId); + ps.setString(2, subgroupId); rs = ps.executeQuery(); - if (rs.next()) { - Map result = new HashMap<>(); - result.put("profileId", rs.getString("PROFILE_ID")); - result.put("profileName", rs.getString("PROFILE_NAME")); - result.put("isExplicitAssignment", false); - return result; + while (rs.next()) { + Map row = new HashMap<>(); + row.put("userId", rs.getString("USER_ID")); + row.put("name", rs.getString("NAME")); + row.put("email", rs.getString("EMAIL")); + row.put("assignedBy", rs.getString("ASSIGNED_BY")); + row.put("assignedAt", rs.getTimestamp("ASSIGNED_AT")); + users.add(row); } } catch (SQLException e) { - classLogger.error("Failed to get default profile", e); + classLogger.error("Failed to get subgroup users", e); } finally { ConnectionUtils.closeAllConnections(ps, rs); } - return null; + return users; } - private static String resolveFeatureId(IRDBMSEngine securityDb, String appId, String featureKey) { - String sql = "SELECT FEATURE_ID FROM APP_FEATURE WHERE APP_ID=? AND FEATURE_KEY=?"; + // ─── Subgroup-Feature Assignment ──────────────────────────────────────── + + public static void setSubgroupFeature(String appId, String subgroupId, String featureId, + boolean enabled, User user) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); PreparedStatement ps = null; - ResultSet rs = null; try { - ps = securityDb.getPreparedStatement(sql); + ps = securityDb.getPreparedStatement( + "DELETE FROM APP_SUBGROUP_FEATURE WHERE APP_ID=? AND SUBGROUP_ID=? AND FEATURE_ID=?"); ps.setString(1, appId); - ps.setString(2, featureKey); - rs = ps.executeQuery(); - if (rs.next()) return rs.getString("FEATURE_ID"); + ps.setString(2, subgroupId); + ps.setString(3, featureId); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); } catch (SQLException e) { - classLogger.error("Failed to resolve feature ID", e); + classLogger.error("Failed to delete existing subgroup feature row", e); } finally { - ConnectionUtils.closeAllConnections(ps, rs); + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); } - return null; - } - - private static boolean queryFeatureEnabled(IRDBMSEngine securityDb, String appId, String profileId, String featureId) { - String sql = "SELECT ENABLED FROM APP_PROFILE_FEATURE WHERE APP_ID=? AND PROFILE_ID=? AND FEATURE_ID=?"; - PreparedStatement ps = null; - ResultSet rs = null; + ps = null; try { - ps = securityDb.getPreparedStatement(sql); + ps = securityDb.getPreparedStatement( + "INSERT INTO APP_SUBGROUP_FEATURE (APP_ID, SUBGROUP_ID, FEATURE_ID, ENABLED) VALUES (?,?,?,?)"); ps.setString(1, appId); - ps.setString(2, profileId); + ps.setString(2, subgroupId); ps.setString(3, featureId); - rs = ps.executeQuery(); - if (rs.next()) return rs.getBoolean("ENABLED"); + ps.setBoolean(4, enabled); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); } catch (SQLException e) { - classLogger.error("Failed to check feature enabled", e); + classLogger.error("Failed to insert subgroup feature", e); + throw new IllegalArgumentException("An error occurred setting the subgroup feature."); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + } + + public static List> getSubgroupFeatures(String appId, String subgroupId) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + List> results = new ArrayList<>(); + Map enabledMap = new HashMap<>(); + String assignSql = "SELECT FEATURE_ID, ENABLED FROM APP_SUBGROUP_FEATURE WHERE APP_ID=? AND SUBGROUP_ID=?"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(assignSql); + ps.setString(1, appId); + ps.setString(2, subgroupId); + rs = ps.executeQuery(); + while (rs.next()) { + enabledMap.put(rs.getString("FEATURE_ID"), rs.getBoolean("ENABLED")); + } + } catch (SQLException e) { + classLogger.error("Failed to get subgroup feature assignments", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + + String featSql = "SELECT FEATURE_ID, FEATURE_KEY, DESCRIPTION FROM APP_FEATURE WHERE APP_ID=? ORDER BY FEATURE_KEY ASC"; + ps = null; + rs = null; + try { + ps = securityDb.getPreparedStatement(featSql); + ps.setString(1, appId); + rs = ps.executeQuery(); + while (rs.next()) { + String featureId = rs.getString("FEATURE_ID"); + Map row = new HashMap<>(); + row.put("featureId", featureId); + row.put("featureKey", rs.getString("FEATURE_KEY")); + row.put("description", rs.getString("DESCRIPTION")); + row.put("enabled", enabledMap.getOrDefault(featureId, Boolean.FALSE)); + results.add(row); + } + } catch (SQLException e) { + classLogger.error("Failed to get subgroup features", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + return results; + } + + // ─── User-Subgroup Assignment ──────────────────────────────────────────── + + public static void assignUserSubgroup(String appId, String userId, String subgroupId, User actor) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + // check for existing assignment + String checkSql = "SELECT COUNT(*) FROM APP_USER_SUBGROUP WHERE APP_ID=? AND USER_ID=? AND SUBGROUP_ID=?"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(checkSql); + ps.setString(1, appId); + ps.setString(2, userId); + ps.setString(3, subgroupId); + rs = ps.executeQuery(); + if (rs.next() && rs.getInt(1) > 0) return; + } catch (SQLException e) { + classLogger.error("Failed to check existing subgroup assignment", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + + String actorId = getUserId(actor); + ps = null; + try { + ps = securityDb.getPreparedStatement( + "INSERT INTO APP_USER_SUBGROUP (APP_ID, USER_ID, SUBGROUP_ID, ASSIGNED_BY, ASSIGNED_AT) VALUES (?,?,?,?,?)"); + ps.setString(1, appId); + ps.setString(2, userId); + ps.setString(3, subgroupId); + ps.setString(4, actorId); + ps.setTimestamp(5, Utility.getCurrentSqlTimestampUTC()); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to assign user to subgroup", e); + throw new IllegalArgumentException("An error occurred assigning the user to the subgroup."); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + } + + public static void removeUserSubgroup(String appId, String userId, String subgroupId) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + PreparedStatement ps = null; + try { + ps = securityDb.getPreparedStatement( + "DELETE FROM APP_USER_SUBGROUP WHERE APP_ID=? AND USER_ID=? AND SUBGROUP_ID=?"); + ps.setString(1, appId); + ps.setString(2, userId); + ps.setString(3, subgroupId); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to remove user from subgroup", e); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + } + + // ─── Profile Manager (delegated BU admin) ──────────────────────────────── + + public static void addProfileManager(String appId, String userId, User actor) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + // check for existing + String checkSql = "SELECT COUNT(*) FROM APP_PROFILE_MANAGER WHERE APP_ID=? AND USER_ID=?"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(checkSql); + ps.setString(1, appId); + ps.setString(2, userId); + rs = ps.executeQuery(); + if (rs.next() && rs.getInt(1) > 0) return; + } catch (SQLException e) { + classLogger.error("Failed to check existing profile manager", e); } finally { ConnectionUtils.closeAllConnections(ps, rs); } + + ps = null; + try { + ps = securityDb.getPreparedStatement( + "INSERT INTO APP_PROFILE_MANAGER (APP_ID, USER_ID, PERMISSION) VALUES (?,?,'assign')"); + ps.setString(1, appId); + ps.setString(2, userId); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to add profile manager", e); + throw new IllegalArgumentException("An error occurred adding the profile manager."); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + } + + public static void removeProfileManager(String appId, String userId) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + PreparedStatement ps = null; + try { + ps = securityDb.getPreparedStatement("DELETE FROM APP_PROFILE_MANAGER WHERE APP_ID=? AND USER_ID=?"); + ps.setString(1, appId); + ps.setString(2, userId); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to remove profile manager", e); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + } + + public static List> getProfileManagers(String appId) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + List> result = new ArrayList<>(); + String sql = "SELECT pm.USER_ID, u.NAME, u.EMAIL, pm.PERMISSION " + + "FROM APP_PROFILE_MANAGER pm " + + "LEFT JOIN SMSS_USER u ON pm.USER_ID = u.ID " + + "WHERE pm.APP_ID=?"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, appId); + rs = ps.executeQuery(); + while (rs.next()) { + Map row = new HashMap<>(); + row.put("userId", rs.getString("USER_ID")); + row.put("name", rs.getString("NAME")); + row.put("email", rs.getString("EMAIL")); + row.put("permission", rs.getString("PERMISSION")); + result.add(row); + } + } catch (SQLException e) { + classLogger.error("Failed to get profile managers", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + return result; + } + + // ─── Feature evaluation ────────────────────────────────────────────────── + + public static boolean checkFeature(String appId, String featureKey, User user) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + String featureId = resolveFeatureId(securityDb, appId, featureKey); + if (featureId == null) return false; + String userId = getUserId(user); + + // Check across all standard profiles + List> profiles = getExplicitUserProfiles(securityDb, appId, userId); + if (profiles.isEmpty()) { + Map defaultProfile = getDefaultProfile(securityDb, appId); + if (defaultProfile != null && !(Boolean) defaultProfile.getOrDefault("isGroup", Boolean.FALSE)) { + if (queryFeatureEnabled(securityDb, appId, (String) defaultProfile.get("profileId"), featureId)) { + return true; + } + } + } else { + for (Map p : profiles) { + if (!(Boolean) p.getOrDefault("isGroup", Boolean.FALSE)) { + if (queryFeatureEnabled(securityDb, appId, (String) p.get("profileId"), featureId)) { + return true; + } + } + } + } + + // Check across all subgroup memberships + List> subgroups = getExplicitUserSubgroups(securityDb, appId, userId); + for (Map sg : subgroups) { + if (querySubgroupFeatureEnabled(securityDb, appId, (String) sg.get("subgroupId"), featureId)) { + return true; + } + } + return false; } - static String getUserId(User user) { + /** + * Returns all enabled features for the calling user, across all profiles and + * subgroup memberships (union). Falls back to the default profile if unassigned. + */ + public static Map getUserFeatures(String appId, User user) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + String userId = getUserId(user); + Map result = new HashMap<>(); + + // Standard profile features + List> explicitProfiles = getExplicitUserProfiles(securityDb, appId, userId); + if (explicitProfiles.isEmpty()) { + Map defaultProfile = getDefaultProfile(securityDb, appId); + if (defaultProfile != null && !(Boolean) defaultProfile.getOrDefault("isGroup", Boolean.FALSE)) { + addProfileFeaturesToResult(securityDb, appId, + (String) defaultProfile.get("profileId"), + (String) defaultProfile.get("profileName"), + true, result); + } + } else { + for (Map p : explicitProfiles) { + if (!(Boolean) p.getOrDefault("isGroup", Boolean.FALSE)) { + addProfileFeaturesToResult(securityDb, appId, + (String) p.get("profileId"), + (String) p.get("profileName"), + false, result); + } + } + } + + // Subgroup features + List> subgroups = getExplicitUserSubgroups(securityDb, appId, userId); + for (Map sg : subgroups) { + addSubgroupFeaturesToResult(securityDb, appId, + (String) sg.get("subgroupId"), + (String) sg.get("subgroupName"), + (String) sg.get("profileName"), + result); + } + + return result; + } + + // ─── Private helpers ───────────────────────────────────────────────────── + + private static void validateFeatureKey(String key) { + if (key == null || key.isEmpty()) { + throw new IllegalArgumentException("Feature key cannot be blank."); + } + if (key.length() > FEATURE_KEY_MAX_LENGTH) { + throw new IllegalArgumentException("Feature key cannot exceed " + FEATURE_KEY_MAX_LENGTH + " characters."); + } + if (!key.matches(FEATURE_KEY_PATTERN)) { + throw new IllegalArgumentException( + "Feature key must contain only alphanumeric characters and hyphens."); + } + } + + private static void clearDefaultProfile(IRDBMSEngine securityDb, String appId) { + PreparedStatement ps = null; + try { + ps = securityDb.getPreparedStatement("UPDATE APP_PROFILE SET IS_DEFAULT=? WHERE APP_ID=?"); + ps.setBoolean(1, false); + ps.setString(2, appId); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to clear default profile", e); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + } + + private static int getAssignedUserCount(IRDBMSEngine securityDb, String appId, String profileId) { + // Count distinct users in APP_USER_PROFILE for this profile + String sql = "SELECT COUNT(DISTINCT USER_ID) FROM APP_USER_PROFILE WHERE APP_ID=? AND PROFILE_ID=?"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, appId); + ps.setString(2, profileId); + rs = ps.executeQuery(); + if (rs.next()) return rs.getInt(1); + } catch (SQLException e) { + classLogger.error("Failed to count profile users", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + return 0; + } + + private static int getSubgroupUserCount(IRDBMSEngine securityDb, String appId, String subgroupId) { + String sql = "SELECT COUNT(*) FROM APP_USER_SUBGROUP WHERE APP_ID=? AND SUBGROUP_ID=?"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, appId); + ps.setString(2, subgroupId); + rs = ps.executeQuery(); + if (rs.next()) return rs.getInt(1); + } catch (SQLException e) { + classLogger.error("Failed to count subgroup users", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + return 0; + } + + private static Map getDefaultProfile(IRDBMSEngine securityDb, String appId) { + String sql = "SELECT PROFILE_ID, PROFILE_NAME, IS_GROUP FROM APP_PROFILE WHERE APP_ID=? AND IS_DEFAULT=TRUE"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, appId); + rs = ps.executeQuery(); + if (rs.next()) { + Map result = new HashMap<>(); + result.put("profileId", rs.getString("PROFILE_ID")); + result.put("profileName", rs.getString("PROFILE_NAME")); + result.put("isGroup", rs.getBoolean("IS_GROUP")); + result.put("isExplicitAssignment", false); + return result; + } + } catch (SQLException e) { + classLogger.error("Failed to get default profile", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + return null; + } + + /** + * Returns all explicit profile assignments for a user (not falling back to default). + */ + private static List> getExplicitUserProfiles(IRDBMSEngine securityDb, + String appId, String userId) { + List> profiles = new ArrayList<>(); + String sql = "SELECT p.PROFILE_ID, p.PROFILE_NAME, p.IS_GROUP " + + "FROM APP_USER_PROFILE up " + + "JOIN APP_PROFILE p ON up.PROFILE_ID = p.PROFILE_ID AND up.APP_ID = p.APP_ID " + + "WHERE up.APP_ID=? AND up.USER_ID=?"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, appId); + ps.setString(2, userId); + rs = ps.executeQuery(); + while (rs.next()) { + Map row = new HashMap<>(); + row.put("profileId", rs.getString("PROFILE_ID")); + row.put("profileName", rs.getString("PROFILE_NAME")); + row.put("isGroup", rs.getBoolean("IS_GROUP")); + profiles.add(row); + } + } catch (SQLException e) { + classLogger.error("Failed to get explicit user profiles", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + return profiles; + } + + /** + * Returns all subgroup assignments for a user, including parent profile name. + */ + private static List> getExplicitUserSubgroups(IRDBMSEngine securityDb, + String appId, String userId) { + List> subgroups = new ArrayList<>(); + String sql = "SELECT us.SUBGROUP_ID, sg.SUBGROUP_NAME, p.PROFILE_NAME " + + "FROM APP_USER_SUBGROUP us " + + "JOIN APP_PROFILE_SUBGROUP sg ON us.SUBGROUP_ID = sg.SUBGROUP_ID " + + "JOIN APP_PROFILE p ON sg.PROFILE_ID = p.PROFILE_ID " + + "WHERE us.APP_ID=? AND us.USER_ID=?"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, appId); + ps.setString(2, userId); + rs = ps.executeQuery(); + while (rs.next()) { + Map row = new HashMap<>(); + row.put("subgroupId", rs.getString("SUBGROUP_ID")); + row.put("subgroupName", rs.getString("SUBGROUP_NAME")); + row.put("profileName", rs.getString("PROFILE_NAME")); + subgroups.add(row); + } + } catch (SQLException e) { + classLogger.error("Failed to get explicit user subgroups", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + return subgroups; + } + + private static void addProfileFeaturesToResult(IRDBMSEngine securityDb, String appId, + String profileId, String profileName, boolean isDefaultProfile, + Map result) { + String sql = "SELECT f.FEATURE_KEY, f.FEATURE_ID " + + "FROM APP_PROFILE_FEATURE pf " + + "JOIN APP_FEATURE f ON pf.FEATURE_ID = f.FEATURE_ID AND pf.APP_ID = f.APP_ID " + + "WHERE pf.APP_ID=? AND pf.PROFILE_ID=? AND pf.ENABLED=TRUE"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, appId); + ps.setString(2, profileId); + rs = ps.executeQuery(); + while (rs.next()) { + String featureKey = rs.getString("FEATURE_KEY"); + if (!result.containsKey(featureKey)) { + Map featureInfo = new HashMap<>(); + featureInfo.put("featureId", rs.getString("FEATURE_ID")); + featureInfo.put("profileName", profileName); + featureInfo.put("isDefaultProfile", isDefaultProfile); + result.put(featureKey, featureInfo); + } + } + } catch (SQLException e) { + classLogger.error("Failed to add profile features to result", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + } + + private static void addSubgroupFeaturesToResult(IRDBMSEngine securityDb, String appId, + String subgroupId, String subgroupName, String parentProfileName, + Map result) { + String sql = "SELECT f.FEATURE_KEY, f.FEATURE_ID " + + "FROM APP_SUBGROUP_FEATURE sf " + + "JOIN APP_FEATURE f ON sf.FEATURE_ID = f.FEATURE_ID AND sf.APP_ID = f.APP_ID " + + "WHERE sf.APP_ID=? AND sf.SUBGROUP_ID=? AND sf.ENABLED=TRUE"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, appId); + ps.setString(2, subgroupId); + rs = ps.executeQuery(); + while (rs.next()) { + String featureKey = rs.getString("FEATURE_KEY"); + if (!result.containsKey(featureKey)) { + Map featureInfo = new HashMap<>(); + featureInfo.put("featureId", rs.getString("FEATURE_ID")); + featureInfo.put("profileName", parentProfileName); + featureInfo.put("subgroupName", subgroupName); + featureInfo.put("isDefaultProfile", false); + result.put(featureKey, featureInfo); + } + } + } catch (SQLException e) { + classLogger.error("Failed to add subgroup features to result", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + } + + private static String resolveFeatureId(IRDBMSEngine securityDb, String appId, String featureKey) { + String sql = "SELECT FEATURE_ID FROM APP_FEATURE WHERE APP_ID=? AND FEATURE_KEY=?"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, appId); + ps.setString(2, featureKey); + rs = ps.executeQuery(); + if (rs.next()) return rs.getString("FEATURE_ID"); + } catch (SQLException e) { + classLogger.error("Failed to resolve feature ID", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + return null; + } + + private static boolean queryFeatureEnabled(IRDBMSEngine securityDb, String appId, + String profileId, String featureId) { + String sql = "SELECT ENABLED FROM APP_PROFILE_FEATURE WHERE APP_ID=? AND PROFILE_ID=? AND FEATURE_ID=?"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, appId); + ps.setString(2, profileId); + ps.setString(3, featureId); + rs = ps.executeQuery(); + if (rs.next()) return rs.getBoolean("ENABLED"); + } catch (SQLException e) { + classLogger.error("Failed to check feature enabled", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + return false; + } + + private static boolean querySubgroupFeatureEnabled(IRDBMSEngine securityDb, String appId, + String subgroupId, String featureId) { + String sql = "SELECT ENABLED FROM APP_SUBGROUP_FEATURE WHERE APP_ID=? AND SUBGROUP_ID=? AND FEATURE_ID=?"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, appId); + ps.setString(2, subgroupId); + ps.setString(3, featureId); + rs = ps.executeQuery(); + if (rs.next()) return rs.getBoolean("ENABLED"); + } catch (SQLException e) { + classLogger.error("Failed to check subgroup feature enabled", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + return false; + } + + private static boolean isGroupProfile(String appId, String profileId) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + String sql = "SELECT IS_GROUP FROM APP_PROFILE WHERE APP_ID=? AND PROFILE_ID=?"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, appId); + ps.setString(2, profileId); + rs = ps.executeQuery(); + if (rs.next()) return rs.getBoolean("IS_GROUP"); + } catch (SQLException e) { + classLogger.error("Failed to check isGroup on profile", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + return false; + } + + private static List getSubgroupIdsForProfile(IRDBMSEngine securityDb, String appId, String profileId) { + List ids = new ArrayList<>(); + String sql = "SELECT SUBGROUP_ID FROM APP_PROFILE_SUBGROUP WHERE APP_ID=? AND PROFILE_ID=?"; + PreparedStatement ps = null; + ResultSet rs = null; + try { + ps = securityDb.getPreparedStatement(sql); + ps.setString(1, appId); + ps.setString(2, profileId); + rs = ps.executeQuery(); + while (rs.next()) ids.add(rs.getString("SUBGROUP_ID")); + } catch (SQLException e) { + classLogger.error("Failed to get subgroup IDs for profile", e); + } finally { + ConnectionUtils.closeAllConnections(ps, rs); + } + return ids; + } + + private static void deleteSubgroupInternal(IRDBMSEngine securityDb, String appId, String subgroupId) { + PreparedStatement ps = null; + try { + ps = securityDb.getPreparedStatement("DELETE FROM APP_USER_SUBGROUP WHERE APP_ID=? AND SUBGROUP_ID=?"); + ps.setString(1, appId); + ps.setString(2, subgroupId); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to cascade delete subgroup user assignments", e); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + ps = null; + try { + ps = securityDb.getPreparedStatement("DELETE FROM APP_SUBGROUP_FEATURE WHERE APP_ID=? AND SUBGROUP_ID=?"); + ps.setString(1, appId); + ps.setString(2, subgroupId); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("Failed to cascade delete subgroup feature assignments", e); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + } + + /** + * Internal-only single-profile lookup, used for backwards compat with old admin reactor. + */ + public static Map getUserProfile(String appId, String userId) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + List> profiles = getExplicitUserProfiles(securityDb, appId, userId); + if (!profiles.isEmpty()) { + Map first = profiles.get(0); + Map result = new HashMap<>(); + result.put("profileId", first.get("profileId")); + result.put("profileName", first.get("profileName")); + result.put("isExplicitAssignment", true); + return result; + } + return getDefaultProfile(securityDb, appId); + } + + public static String getUserId(User user) { if (user == null) return null; AccessToken token = user.getAccessToken(user.getPrimaryLogin()); return token != null ? token.getId() : null; diff --git a/src/prerna/auth/utils/SecurityOwlCreator.java b/src/prerna/auth/utils/SecurityOwlCreator.java index 89c540ea7d7..2ad8fe56053 100644 --- a/src/prerna/auth/utils/SecurityOwlCreator.java +++ b/src/prerna/auth/utils/SecurityOwlCreator.java @@ -46,6 +46,13 @@ public class SecurityOwlCreator extends AbstractOwlCreator { static { relationshipsRequired.add( new String[] { "GITHUB_APP", "GITHUB_PROJECT_LINK", "GITHUB_APP.APP_ID.GITHUB_PROJECT_LINK.APP_ID" }); + // Profile system — presence of this relation signals a rebuilt OWL is needed + relationshipsRequired.add( + new String[] { "APP_PROFILE", "APP_USER_PROFILE", "APP_PROFILE.PROFILE_ID.APP_USER_PROFILE.PROFILE_ID" }); + relationshipsRequired.add( + new String[] { "PLATFORM_PROFILE", "PLATFORM_USER_PROFILE", "PLATFORM_PROFILE.PROFILE_ID.PLATFORM_USER_PROFILE.PROFILE_ID" }); + relationshipsRequired.add( + new String[] { "APP_PROFILE", "APP_PROFILE_SUBGROUP", "APP_PROFILE.PROFILE_ID.APP_PROFILE_SUBGROUP.PROFILE_ID" }); } public SecurityOwlCreator(AbstractSqlQueryUtil queryUtil) { @@ -433,6 +440,84 @@ public void createColumnsAndTypes(AbstractSqlQueryUtil queryUtil) { Pair.with("CREATED_ON", TIMESTAMP_DATATYPE_NAME), Pair.with("UPDATED_ON", TIMESTAMP_DATATYPE_NAME))); + // ─── App Profile system ─────────────────────────────────────────────────── + addTable("APP_PROFILE", Arrays.asList( + Pair.with("PROFILE_ID", VARCHAR_255), + Pair.with("APP_ID", VARCHAR_255), + Pair.with("PROFILE_NAME", VARCHAR_255), + Pair.with("DESCRIPTION", CLOB_DATATYPE_NAME), + Pair.with("IS_DEFAULT", BOOLEAN_DATATYPE_NAME), + Pair.with("IS_GROUP", BOOLEAN_DATATYPE_NAME), + Pair.with("CREATED_BY", VARCHAR_255), + Pair.with("CREATED_AT", TIMESTAMP_DATATYPE_NAME))); + + addTable("APP_FEATURE", Arrays.asList( + Pair.with("FEATURE_ID", VARCHAR_255), + Pair.with("APP_ID", VARCHAR_255), + Pair.with("FEATURE_KEY", VARCHAR_255), + Pair.with("DESCRIPTION", CLOB_DATATYPE_NAME), + Pair.with("CREATED_BY", VARCHAR_255), + Pair.with("CREATED_AT", TIMESTAMP_DATATYPE_NAME))); + + addTable("APP_PROFILE_FEATURE", Arrays.asList( + Pair.with("APP_ID", VARCHAR_255), + Pair.with("PROFILE_ID", VARCHAR_255), + Pair.with("FEATURE_ID", VARCHAR_255), + Pair.with("ENABLED", BOOLEAN_DATATYPE_NAME))); + + addTable("APP_USER_PROFILE", Arrays.asList( + Pair.with("APP_ID", VARCHAR_255), + Pair.with("USER_ID", VARCHAR_255), + Pair.with("PROFILE_ID", VARCHAR_255), + Pair.with("ASSIGNED_BY", VARCHAR_255), + Pair.with("ASSIGNED_AT", TIMESTAMP_DATATYPE_NAME))); + + addTable("APP_PROFILE_SUBGROUP", Arrays.asList( + Pair.with("SUBGROUP_ID", VARCHAR_255), + Pair.with("PROFILE_ID", VARCHAR_255), + Pair.with("APP_ID", VARCHAR_255), + Pair.with("SUBGROUP_NAME", VARCHAR_255), + Pair.with("DESCRIPTION", CLOB_DATATYPE_NAME), + Pair.with("CREATED_BY", VARCHAR_255), + Pair.with("CREATED_AT", TIMESTAMP_DATATYPE_NAME))); + + addTable("APP_SUBGROUP_FEATURE", Arrays.asList( + Pair.with("APP_ID", VARCHAR_255), + Pair.with("SUBGROUP_ID", VARCHAR_255), + Pair.with("FEATURE_ID", VARCHAR_255), + Pair.with("ENABLED", BOOLEAN_DATATYPE_NAME))); + + addTable("APP_USER_SUBGROUP", Arrays.asList( + Pair.with("APP_ID", VARCHAR_255), + Pair.with("USER_ID", VARCHAR_255), + Pair.with("SUBGROUP_ID", VARCHAR_255), + Pair.with("ASSIGNED_BY", VARCHAR_255), + Pair.with("ASSIGNED_AT", TIMESTAMP_DATATYPE_NAME))); + + addTable("APP_PROFILE_MANAGER", Arrays.asList( + Pair.with("APP_ID", VARCHAR_255), + Pair.with("USER_ID", VARCHAR_255), + Pair.with("PERMISSION", "VARCHAR(50)"))); + + // ─── Platform Profile system ───────────────────────────────────────────── + addTable("PLATFORM_PROFILE", Arrays.asList( + Pair.with("PROFILE_ID", VARCHAR_255), + Pair.with("PROFILE_NAME", VARCHAR_255), + Pair.with("DESCRIPTION", CLOB_DATATYPE_NAME), + Pair.with("CREATED_BY", VARCHAR_255), + Pair.with("CREATED_AT", TIMESTAMP_DATATYPE_NAME))); + + addTable("PLATFORM_PROFILE_FEATURE", Arrays.asList( + Pair.with("PROFILE_ID", VARCHAR_255), + Pair.with("FEATURE_KEY", "VARCHAR(100)"), + Pair.with("ENABLED", BOOLEAN_DATATYPE_NAME))); + + addTable("PLATFORM_USER_PROFILE", Arrays.asList( + Pair.with("USER_ID", VARCHAR_255), + Pair.with("PROFILE_ID", VARCHAR_255), + Pair.with("ASSIGNED_BY", VARCHAR_255), + Pair.with("ASSIGNED_AT", TIMESTAMP_DATATYPE_NAME))); + // "ENGINEMETAKEYS", "PROJECTMETAKEYS", "INSIGHTMETAKEYS", "USERMETAKEYS" // all have the same columns and default values List metaKeyTableNames = Arrays.asList(Constants.ENGINE_METAKEYS, Constants.PROJECT_METAKEYS, @@ -498,6 +583,24 @@ protected void writeRelations(WriteOWLEngine owler) throws Exception { // github app integration joins owler.addRelation("GITHUB_APP", "GITHUB_PROJECT_LINK", "GITHUB_APP.APP_ID.GITHUB_PROJECT_LINK.APP_ID"); owler.addRelation("PROJECT", "GITHUB_PROJECT_LINK", "PROJECT.PROJECTID.GITHUB_PROJECT_LINK.PROJECT_ID"); + + // app profile joins + owler.addRelation("PROJECT", "APP_PROFILE", "PROJECT.PROJECTID.APP_PROFILE.APP_ID"); + owler.addRelation("APP_PROFILE", "APP_USER_PROFILE", "APP_PROFILE.PROFILE_ID.APP_USER_PROFILE.PROFILE_ID"); + owler.addRelation("APP_PROFILE", "APP_PROFILE_FEATURE", "APP_PROFILE.PROFILE_ID.APP_PROFILE_FEATURE.PROFILE_ID"); + owler.addRelation("APP_FEATURE", "APP_PROFILE_FEATURE", "APP_FEATURE.FEATURE_ID.APP_PROFILE_FEATURE.FEATURE_ID"); + owler.addRelation("SMSS_USER", "APP_USER_PROFILE", "SMSS_USER.ID.APP_USER_PROFILE.USER_ID"); + owler.addRelation("APP_PROFILE", "APP_PROFILE_SUBGROUP", "APP_PROFILE.PROFILE_ID.APP_PROFILE_SUBGROUP.PROFILE_ID"); + owler.addRelation("APP_PROFILE_SUBGROUP", "APP_USER_SUBGROUP", "APP_PROFILE_SUBGROUP.SUBGROUP_ID.APP_USER_SUBGROUP.SUBGROUP_ID"); + owler.addRelation("APP_PROFILE_SUBGROUP", "APP_SUBGROUP_FEATURE", "APP_PROFILE_SUBGROUP.SUBGROUP_ID.APP_SUBGROUP_FEATURE.SUBGROUP_ID"); + owler.addRelation("APP_FEATURE", "APP_SUBGROUP_FEATURE", "APP_FEATURE.FEATURE_ID.APP_SUBGROUP_FEATURE.FEATURE_ID"); + owler.addRelation("SMSS_USER", "APP_USER_SUBGROUP", "SMSS_USER.ID.APP_USER_SUBGROUP.USER_ID"); + owler.addRelation("SMSS_USER", "APP_PROFILE_MANAGER", "SMSS_USER.ID.APP_PROFILE_MANAGER.USER_ID"); + + // platform profile joins + owler.addRelation("PLATFORM_PROFILE", "PLATFORM_USER_PROFILE", "PLATFORM_PROFILE.PROFILE_ID.PLATFORM_USER_PROFILE.PROFILE_ID"); + owler.addRelation("PLATFORM_PROFILE", "PLATFORM_PROFILE_FEATURE", "PLATFORM_PROFILE.PROFILE_ID.PLATFORM_PROFILE_FEATURE.PROFILE_ID"); + owler.addRelation("SMSS_USER", "PLATFORM_USER_PROFILE", "SMSS_USER.ID.PLATFORM_USER_PROFILE.USER_ID"); } @Override diff --git a/src/prerna/reactor/appprofile/AddAppProfileManagerReactor.java b/src/prerna/reactor/appprofile/AddAppProfileManagerReactor.java new file mode 100644 index 00000000000..33f6945a1cc --- /dev/null +++ b/src/prerna/reactor/appprofile/AddAppProfileManagerReactor.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; + +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class AddAppProfileManagerReactor extends AbstractReactor { + + private static final Logger classLogger = LogManager.getLogger(AddAppProfileManagerReactor.class); + + public AddAppProfileManagerReactor() { + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.USER_ID.getKey() }; + this.keyRequired = new int[] { 1, 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + String userId = this.keyValue.get(ReactorKeysEnum.USER_ID.getKey()); + + if (!AppProfileUtils.canManageProfiles(user, appId)) { + throw new IllegalArgumentException("Only app owners/editors can grant profile manager permissions."); + } + AppProfileUtils.addProfileManager(appId, userId, user); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Profile manager added.")); + return noun; + } + + @Override + public String getReactorDescription() { + return "Grant a user delegated 'assign' permission to add/remove users from profiles."; + } +} diff --git a/src/prerna/reactor/appprofile/AssignAppUserProfileReactor.java b/src/prerna/reactor/appprofile/AssignAppUserProfileReactor.java new file mode 100644 index 00000000000..9ebb924c6e4 --- /dev/null +++ b/src/prerna/reactor/appprofile/AssignAppUserProfileReactor.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class AssignAppUserProfileReactor extends AbstractReactor { + + private static final Logger classLogger = LogManager.getLogger(AssignAppUserProfileReactor.class); + + public AssignAppUserProfileReactor() { + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.USER_ID.getKey(), ReactorKeysEnum.PROFILE_ID.getKey() }; + this.keyRequired = new int[] { 1, 1, 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + String userId = this.keyValue.get(ReactorKeysEnum.USER_ID.getKey()); + String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); + + if (!AppProfileUtils.canAssignProfiles(user, appId)) { + throw new IllegalArgumentException("User does not have permission to assign profiles for this app."); + } + AppProfileUtils.assignUserProfile(appId, userId, profileId, user); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("User assigned to profile.")); + return noun; + } + + @Override + public String getReactorDescription() { + return "Assign a user to a profile for an app. A user can be in multiple profiles simultaneously."; + } +} diff --git a/src/prerna/reactor/appprofile/AssignAppUserSubgroupReactor.java b/src/prerna/reactor/appprofile/AssignAppUserSubgroupReactor.java new file mode 100644 index 00000000000..6878828b3c1 --- /dev/null +++ b/src/prerna/reactor/appprofile/AssignAppUserSubgroupReactor.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class AssignAppUserSubgroupReactor extends AbstractReactor { + + private static final Logger classLogger = LogManager.getLogger(AssignAppUserSubgroupReactor.class); + + public AssignAppUserSubgroupReactor() { + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.USER_ID.getKey(), ReactorKeysEnum.SUBGROUP_ID.getKey() }; + this.keyRequired = new int[] { 1, 1, 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + String userId = this.keyValue.get(ReactorKeysEnum.USER_ID.getKey()); + String subgroupId = this.keyValue.get(ReactorKeysEnum.SUBGROUP_ID.getKey()); + + if (!AppProfileUtils.canAssignProfiles(user, appId)) { + throw new IllegalArgumentException("User does not have permission to assign users for this app."); + } + AppProfileUtils.assignUserSubgroup(appId, userId, subgroupId, user); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("User assigned to subgroup.")); + return noun; + } + + @Override + public String getReactorDescription() { + return "Assign a user to a sub-group."; + } +} diff --git a/src/prerna/reactor/appprofile/AssignUserProfileReactor.java b/src/prerna/reactor/appprofile/AssignUserProfileReactor.java index 5f9ec1f00bd..084177c5a05 100644 --- a/src/prerna/reactor/appprofile/AssignUserProfileReactor.java +++ b/src/prerna/reactor/appprofile/AssignUserProfileReactor.java @@ -27,16 +27,27 @@ *******************************************************************************/ package prerna.reactor.appprofile; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import prerna.auth.User; import prerna.auth.utils.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * @deprecated Use {@link AssignAppUserProfileReactor} (Pixel: AssignAppUserProfile). + * Kept for backwards compatibility. + */ +@Deprecated public class AssignUserProfileReactor extends AbstractReactor { + private static final Logger classLogger = LogManager.getLogger(AssignUserProfileReactor.class); + public AssignUserProfileReactor() { - this.keysToGet = new String[] { "app", "userId", "profileId" }; + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.USER_ID.getKey(), ReactorKeysEnum.PROFILE_ID.getKey() }; this.keyRequired = new int[] { 1, 1, 1 }; } @@ -44,21 +55,21 @@ public AssignUserProfileReactor() { public NounMetadata execute() { organizeKeys(); User user = this.insight.getUser(); - String appId = this.keyValue.get("app"); - String userId = this.keyValue.get("userId"); - String profileId = this.keyValue.get("profileId"); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + String userId = this.keyValue.get(ReactorKeysEnum.USER_ID.getKey()); + String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); - if (!AppProfileUtils.canManageProfiles(user, appId)) { - throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); + if (!AppProfileUtils.canAssignProfiles(user, appId)) { + throw new IllegalArgumentException("User does not have permission to assign profiles for this app."); } AppProfileUtils.assignUserProfile(appId, userId, profileId, user); - NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("User assigned to profile.")); return noun; } @Override public String getReactorDescription() { - return "Assign a user to a profile for an app."; + return "Assign a user to a profile for an app. A user can be in multiple profiles simultaneously."; } } diff --git a/src/prerna/reactor/appprofile/CheckAppFeatureReactor.java b/src/prerna/reactor/appprofile/CheckAppFeatureReactor.java new file mode 100644 index 00000000000..9f7a24b821e --- /dev/null +++ b/src/prerna/reactor/appprofile/CheckAppFeatureReactor.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class CheckAppFeatureReactor extends AbstractReactor { + + private static final Logger classLogger = LogManager.getLogger(CheckAppFeatureReactor.class); + + public CheckAppFeatureReactor() { + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.FEATURE_KEY.getKey() }; + this.keyRequired = new int[] { 1, 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + String featureKey = this.keyValue.get(ReactorKeysEnum.FEATURE_KEY.getKey()); + + if (!AppProfileUtils.canEvaluateFeatures(user, appId)) { + throw new IllegalArgumentException("User does not have access to this app."); + } + boolean enabled = AppProfileUtils.checkFeature(appId, featureKey, user); + return new NounMetadata(enabled, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); + } + + @Override + public String getReactorDescription() { + return "Check whether a feature is enabled for the calling user in an app, across all assigned profiles and subgroups."; + } +} diff --git a/src/prerna/reactor/appprofile/CheckFeatureReactor.java b/src/prerna/reactor/appprofile/CheckFeatureReactor.java index 3732a95955a..2d61b608b71 100644 --- a/src/prerna/reactor/appprofile/CheckFeatureReactor.java +++ b/src/prerna/reactor/appprofile/CheckFeatureReactor.java @@ -27,16 +27,26 @@ *******************************************************************************/ package prerna.reactor.appprofile; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import prerna.auth.User; import prerna.auth.utils.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * @deprecated Use {@link CheckAppFeatureReactor} (Pixel: CheckAppFeature). + */ +@Deprecated public class CheckFeatureReactor extends AbstractReactor { + private static final Logger classLogger = LogManager.getLogger(CheckFeatureReactor.class); + public CheckFeatureReactor() { - this.keysToGet = new String[] { "app", "featureKey" }; + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.FEATURE_KEY.getKey() }; this.keyRequired = new int[] { 1, 1 }; } @@ -44,14 +54,14 @@ public CheckFeatureReactor() { public NounMetadata execute() { organizeKeys(); User user = this.insight.getUser(); - String appId = this.keyValue.get("app"); - String featureKey = this.keyValue.get("featureKey"); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + String featureKey = this.keyValue.get(ReactorKeysEnum.FEATURE_KEY.getKey()); if (!AppProfileUtils.canEvaluateFeatures(user, appId)) { throw new IllegalArgumentException("User does not have access to this app."); } boolean enabled = AppProfileUtils.checkFeature(appId, featureKey, user); - return new NounMetadata(enabled, PixelDataType.BOOLEAN); + return new NounMetadata(enabled, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); } @Override diff --git a/src/prerna/reactor/appprofile/CreateAppFeatureReactor.java b/src/prerna/reactor/appprofile/CreateAppFeatureReactor.java index d4e7408ccf1..63c028b2161 100644 --- a/src/prerna/reactor/appprofile/CreateAppFeatureReactor.java +++ b/src/prerna/reactor/appprofile/CreateAppFeatureReactor.java @@ -29,16 +29,22 @@ import java.util.Map; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import prerna.auth.User; import prerna.auth.utils.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; public class CreateAppFeatureReactor extends AbstractReactor { + private static final Logger classLogger = LogManager.getLogger(CreateAppFeatureReactor.class); + public CreateAppFeatureReactor() { - this.keysToGet = new String[] { "app", "key", "description" }; + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.FEATURE_KEY.getKey(), ReactorKeysEnum.DESCRIPTION.getKey() }; this.keyRequired = new int[] { 1, 1, 0 }; } @@ -46,15 +52,15 @@ public CreateAppFeatureReactor() { public NounMetadata execute() { organizeKeys(); User user = this.insight.getUser(); - String appId = this.keyValue.get("app"); - String featureKey = this.keyValue.get("key"); - String description = this.keyValue.get("description"); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + String featureKey = this.keyValue.get(ReactorKeysEnum.FEATURE_KEY.getKey()); + String description = this.keyValue.get(ReactorKeysEnum.DESCRIPTION.getKey()); if (!AppProfileUtils.canManageProfiles(user, appId)) { throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); } Map result = AppProfileUtils.createFeature(appId, featureKey, description, user); - NounMetadata noun = new NounMetadata(result, PixelDataType.MAP); + NounMetadata noun = new NounMetadata(result, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Feature created.")); return noun; } diff --git a/src/prerna/reactor/appprofile/CreateAppProfileReactor.java b/src/prerna/reactor/appprofile/CreateAppProfileReactor.java index ec13f3ba4fd..a1790b5ccfe 100644 --- a/src/prerna/reactor/appprofile/CreateAppProfileReactor.java +++ b/src/prerna/reactor/appprofile/CreateAppProfileReactor.java @@ -29,33 +29,40 @@ import java.util.Map; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import prerna.auth.User; import prerna.auth.utils.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; public class CreateAppProfileReactor extends AbstractReactor { + private static final Logger classLogger = LogManager.getLogger(CreateAppProfileReactor.class); + public CreateAppProfileReactor() { - this.keysToGet = new String[] { "app", "name", "description", "isDefault" }; - this.keyRequired = new int[] { 1, 1, 0, 0 }; + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.NAME.getKey(), ReactorKeysEnum.DESCRIPTION.getKey(), ReactorKeysEnum.IS_DEFAULT.getKey(), ReactorKeysEnum.IS_GROUP.getKey() }; + this.keyRequired = new int[] { 1, 1, 0, 0, 0 }; } @Override public NounMetadata execute() { organizeKeys(); User user = this.insight.getUser(); - String appId = this.keyValue.get("app"); - String name = this.keyValue.get("name"); - String description = this.keyValue.get("description"); - boolean isDefault = Boolean.parseBoolean(this.keyValue.get("isDefault")); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + String name = this.keyValue.get(ReactorKeysEnum.NAME.getKey()); + String description = this.keyValue.get(ReactorKeysEnum.DESCRIPTION.getKey()); + boolean isDefault = Boolean.parseBoolean(this.keyValue.get(ReactorKeysEnum.IS_DEFAULT.getKey())); + boolean isGroup = Boolean.parseBoolean(this.keyValue.get(ReactorKeysEnum.IS_GROUP.getKey())); if (!AppProfileUtils.canManageProfiles(user, appId)) { throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); } - Map result = AppProfileUtils.createProfile(appId, name, description, isDefault, user); - NounMetadata noun = new NounMetadata(result, PixelDataType.MAP); + Map result = AppProfileUtils.createProfile(appId, name, description, isDefault, isGroup, user); + NounMetadata noun = new NounMetadata(result, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Profile created.")); return noun; } diff --git a/src/prerna/reactor/appprofile/CreateAppSubgroupReactor.java b/src/prerna/reactor/appprofile/CreateAppSubgroupReactor.java new file mode 100644 index 00000000000..b5c861c5dfe --- /dev/null +++ b/src/prerna/reactor/appprofile/CreateAppSubgroupReactor.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import java.util.Map; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class CreateAppSubgroupReactor extends AbstractReactor { + + private static final Logger classLogger = LogManager.getLogger(CreateAppSubgroupReactor.class); + + public CreateAppSubgroupReactor() { + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.PROFILE_ID.getKey(), ReactorKeysEnum.NAME.getKey(), ReactorKeysEnum.DESCRIPTION.getKey() }; + this.keyRequired = new int[] { 1, 1, 1, 0 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); + String name = this.keyValue.get(ReactorKeysEnum.NAME.getKey()); + String description = this.keyValue.get(ReactorKeysEnum.DESCRIPTION.getKey()); + + if (!AppProfileUtils.canManageProfiles(user, appId)) { + throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); + } + Map result = AppProfileUtils.createSubgroup(appId, profileId, name, description, user); + NounMetadata noun = new NounMetadata(result, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Subgroup created.")); + return noun; + } + + @Override + public String getReactorDescription() { + return "Create a named sub-group within a group-style profile."; + } +} diff --git a/src/prerna/reactor/appprofile/DeleteAppFeatureReactor.java b/src/prerna/reactor/appprofile/DeleteAppFeatureReactor.java index 40968e04d17..6688655ee81 100644 --- a/src/prerna/reactor/appprofile/DeleteAppFeatureReactor.java +++ b/src/prerna/reactor/appprofile/DeleteAppFeatureReactor.java @@ -27,16 +27,22 @@ *******************************************************************************/ package prerna.reactor.appprofile; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import prerna.auth.User; import prerna.auth.utils.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; public class DeleteAppFeatureReactor extends AbstractReactor { + private static final Logger classLogger = LogManager.getLogger(DeleteAppFeatureReactor.class); + public DeleteAppFeatureReactor() { - this.keysToGet = new String[] { "app", "featureId" }; + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.FEATURE_ID.getKey() }; this.keyRequired = new int[] { 1, 1 }; } @@ -44,14 +50,14 @@ public DeleteAppFeatureReactor() { public NounMetadata execute() { organizeKeys(); User user = this.insight.getUser(); - String appId = this.keyValue.get("app"); - String featureId = this.keyValue.get("featureId"); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + String featureId = this.keyValue.get(ReactorKeysEnum.FEATURE_ID.getKey()); if (!AppProfileUtils.canManageProfiles(user, appId)) { throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); } AppProfileUtils.deleteFeature(appId, featureId, user); - NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Feature deleted.")); return noun; } diff --git a/src/prerna/reactor/appprofile/DeleteAppProfileReactor.java b/src/prerna/reactor/appprofile/DeleteAppProfileReactor.java index 41741010f69..35a522b54ce 100644 --- a/src/prerna/reactor/appprofile/DeleteAppProfileReactor.java +++ b/src/prerna/reactor/appprofile/DeleteAppProfileReactor.java @@ -27,16 +27,22 @@ *******************************************************************************/ package prerna.reactor.appprofile; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import prerna.auth.User; import prerna.auth.utils.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; public class DeleteAppProfileReactor extends AbstractReactor { + private static final Logger classLogger = LogManager.getLogger(DeleteAppProfileReactor.class); + public DeleteAppProfileReactor() { - this.keysToGet = new String[] { "app", "profileId" }; + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.PROFILE_ID.getKey() }; this.keyRequired = new int[] { 1, 1 }; } @@ -44,14 +50,14 @@ public DeleteAppProfileReactor() { public NounMetadata execute() { organizeKeys(); User user = this.insight.getUser(); - String appId = this.keyValue.get("app"); - String profileId = this.keyValue.get("profileId"); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); if (!AppProfileUtils.canManageProfiles(user, appId)) { throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); } AppProfileUtils.deleteProfile(appId, profileId, user); - NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Profile deleted.")); return noun; } diff --git a/src/prerna/reactor/appprofile/DeleteAppSubgroupReactor.java b/src/prerna/reactor/appprofile/DeleteAppSubgroupReactor.java new file mode 100644 index 00000000000..0898afeb3b7 --- /dev/null +++ b/src/prerna/reactor/appprofile/DeleteAppSubgroupReactor.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class DeleteAppSubgroupReactor extends AbstractReactor { + + private static final Logger classLogger = LogManager.getLogger(DeleteAppSubgroupReactor.class); + + public DeleteAppSubgroupReactor() { + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.SUBGROUP_ID.getKey() }; + this.keyRequired = new int[] { 1, 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + String subgroupId = this.keyValue.get(ReactorKeysEnum.SUBGROUP_ID.getKey()); + + if (!AppProfileUtils.canManageProfiles(user, appId)) { + throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); + } + AppProfileUtils.deleteSubgroup(appId, subgroupId, user); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Subgroup deleted.")); + return noun; + } + + @Override + public String getReactorDescription() { + return "Delete a sub-group and its user/feature assignments."; + } +} diff --git a/src/prerna/reactor/appprofile/GetAppFeaturesReactor.java b/src/prerna/reactor/appprofile/GetAppFeaturesReactor.java index 24ffff4f5e4..2210759e113 100644 --- a/src/prerna/reactor/appprofile/GetAppFeaturesReactor.java +++ b/src/prerna/reactor/appprofile/GetAppFeaturesReactor.java @@ -30,16 +30,22 @@ import java.util.List; import java.util.Map; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import prerna.auth.User; import prerna.auth.utils.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; public class GetAppFeaturesReactor extends AbstractReactor { + private static final Logger classLogger = LogManager.getLogger(GetAppFeaturesReactor.class); + public GetAppFeaturesReactor() { - this.keysToGet = new String[] { "app" }; + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey() }; this.keyRequired = new int[] { 1 }; } @@ -47,13 +53,13 @@ public GetAppFeaturesReactor() { public NounMetadata execute() { organizeKeys(); User user = this.insight.getUser(); - String appId = this.keyValue.get("app"); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); if (!AppProfileUtils.canManageProfiles(user, appId)) { throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); } List> features = AppProfileUtils.getFeatures(appId); - return new NounMetadata(features, PixelDataType.CUSTOM_DATA_STRUCTURE); + return new NounMetadata(features, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); } @Override diff --git a/src/prerna/reactor/appprofile/GetProfileFeaturesReactor.java b/src/prerna/reactor/appprofile/GetAppProfileFeaturesReactor.java similarity index 78% rename from src/prerna/reactor/appprofile/GetProfileFeaturesReactor.java rename to src/prerna/reactor/appprofile/GetAppProfileFeaturesReactor.java index 3dc158629c6..8963d300438 100644 --- a/src/prerna/reactor/appprofile/GetProfileFeaturesReactor.java +++ b/src/prerna/reactor/appprofile/GetAppProfileFeaturesReactor.java @@ -30,16 +30,23 @@ import java.util.List; import java.util.Map; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; + import prerna.auth.User; import prerna.auth.utils.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.nounmeta.NounMetadata; -public class GetProfileFeaturesReactor extends AbstractReactor { +public class GetAppProfileFeaturesReactor extends AbstractReactor { + + private static final Logger classLogger = LogManager.getLogger(GetAppProfileFeaturesReactor.class); - public GetProfileFeaturesReactor() { - this.keysToGet = new String[] { "app", "profileId" }; + public GetAppProfileFeaturesReactor() { + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.PROFILE_ID.getKey() }; this.keyRequired = new int[] { 1, 1 }; } @@ -47,14 +54,14 @@ public GetProfileFeaturesReactor() { public NounMetadata execute() { organizeKeys(); User user = this.insight.getUser(); - String appId = this.keyValue.get("app"); - String profileId = this.keyValue.get("profileId"); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); if (!AppProfileUtils.canManageProfiles(user, appId)) { throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); } List> features = AppProfileUtils.getProfileFeatures(appId, profileId); - return new NounMetadata(features, PixelDataType.CUSTOM_DATA_STRUCTURE); + return new NounMetadata(features, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); } @Override diff --git a/src/prerna/reactor/appprofile/GetAppProfileManagersReactor.java b/src/prerna/reactor/appprofile/GetAppProfileManagersReactor.java new file mode 100644 index 00000000000..7d7d6eacaca --- /dev/null +++ b/src/prerna/reactor/appprofile/GetAppProfileManagersReactor.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import java.util.List; +import java.util.Map; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; + +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class GetAppProfileManagersReactor extends AbstractReactor { + + private static final Logger classLogger = LogManager.getLogger(GetAppProfileManagersReactor.class); + + public GetAppProfileManagersReactor() { + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey() }; + this.keyRequired = new int[] { 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + + if (!AppProfileUtils.canManageProfiles(user, appId)) { + throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); + } + List> result = AppProfileUtils.getProfileManagers(appId); + return new NounMetadata(result, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); + } + + @Override + public String getReactorDescription() { + return "Get all users with delegated profile manager permission for an app."; + } +} diff --git a/src/prerna/reactor/appprofile/GetProfileUsersReactor.java b/src/prerna/reactor/appprofile/GetAppProfileUsersReactor.java similarity index 78% rename from src/prerna/reactor/appprofile/GetProfileUsersReactor.java rename to src/prerna/reactor/appprofile/GetAppProfileUsersReactor.java index 0ff41982a80..50c16e2686a 100644 --- a/src/prerna/reactor/appprofile/GetProfileUsersReactor.java +++ b/src/prerna/reactor/appprofile/GetAppProfileUsersReactor.java @@ -30,16 +30,23 @@ import java.util.List; import java.util.Map; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; + import prerna.auth.User; import prerna.auth.utils.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.nounmeta.NounMetadata; -public class GetProfileUsersReactor extends AbstractReactor { +public class GetAppProfileUsersReactor extends AbstractReactor { + + private static final Logger classLogger = LogManager.getLogger(GetAppProfileUsersReactor.class); - public GetProfileUsersReactor() { - this.keysToGet = new String[] { "app", "profileId" }; + public GetAppProfileUsersReactor() { + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.PROFILE_ID.getKey() }; this.keyRequired = new int[] { 1, 1 }; } @@ -47,14 +54,14 @@ public GetProfileUsersReactor() { public NounMetadata execute() { organizeKeys(); User user = this.insight.getUser(); - String appId = this.keyValue.get("app"); - String profileId = this.keyValue.get("profileId"); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); if (!AppProfileUtils.canManageProfiles(user, appId)) { throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); } List> users = AppProfileUtils.getProfileUsers(appId, profileId); - return new NounMetadata(users, PixelDataType.CUSTOM_DATA_STRUCTURE); + return new NounMetadata(users, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); } @Override diff --git a/src/prerna/reactor/appprofile/GetAppProfilesReactor.java b/src/prerna/reactor/appprofile/GetAppProfilesReactor.java index 674e206b3b7..6cbea525da5 100644 --- a/src/prerna/reactor/appprofile/GetAppProfilesReactor.java +++ b/src/prerna/reactor/appprofile/GetAppProfilesReactor.java @@ -30,16 +30,22 @@ import java.util.List; import java.util.Map; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import prerna.auth.User; import prerna.auth.utils.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; public class GetAppProfilesReactor extends AbstractReactor { + private static final Logger classLogger = LogManager.getLogger(GetAppProfilesReactor.class); + public GetAppProfilesReactor() { - this.keysToGet = new String[] { "app" }; + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey() }; this.keyRequired = new int[] { 1 }; } @@ -47,13 +53,13 @@ public GetAppProfilesReactor() { public NounMetadata execute() { organizeKeys(); User user = this.insight.getUser(); - String appId = this.keyValue.get("app"); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); if (!AppProfileUtils.canManageProfiles(user, appId)) { throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); } List> profiles = AppProfileUtils.getProfiles(appId); - return new NounMetadata(profiles, PixelDataType.CUSTOM_DATA_STRUCTURE); + return new NounMetadata(profiles, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); } @Override diff --git a/src/prerna/reactor/appprofile/GetAppSubgroupFeaturesReactor.java b/src/prerna/reactor/appprofile/GetAppSubgroupFeaturesReactor.java new file mode 100644 index 00000000000..bdb13d61669 --- /dev/null +++ b/src/prerna/reactor/appprofile/GetAppSubgroupFeaturesReactor.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import java.util.List; +import java.util.Map; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; + +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class GetAppSubgroupFeaturesReactor extends AbstractReactor { + + private static final Logger classLogger = LogManager.getLogger(GetAppSubgroupFeaturesReactor.class); + + public GetAppSubgroupFeaturesReactor() { + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.SUBGROUP_ID.getKey() }; + this.keyRequired = new int[] { 1, 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + String subgroupId = this.keyValue.get(ReactorKeysEnum.SUBGROUP_ID.getKey()); + + if (!AppProfileUtils.canManageProfiles(user, appId)) { + throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); + } + List> result = AppProfileUtils.getSubgroupFeatures(appId, subgroupId); + return new NounMetadata(result, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); + } + + @Override + public String getReactorDescription() { + return "Get all feature flags for a sub-group."; + } +} diff --git a/src/prerna/reactor/appprofile/GetAppSubgroupUsersReactor.java b/src/prerna/reactor/appprofile/GetAppSubgroupUsersReactor.java new file mode 100644 index 00000000000..54450e45d33 --- /dev/null +++ b/src/prerna/reactor/appprofile/GetAppSubgroupUsersReactor.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import java.util.List; +import java.util.Map; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; + +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class GetAppSubgroupUsersReactor extends AbstractReactor { + + private static final Logger classLogger = LogManager.getLogger(GetAppSubgroupUsersReactor.class); + + public GetAppSubgroupUsersReactor() { + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.SUBGROUP_ID.getKey() }; + this.keyRequired = new int[] { 1, 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + String subgroupId = this.keyValue.get(ReactorKeysEnum.SUBGROUP_ID.getKey()); + + if (!AppProfileUtils.canAssignProfiles(user, appId)) { + throw new IllegalArgumentException("User does not have permission to view subgroup users for this app."); + } + List> result = AppProfileUtils.getSubgroupUsers(appId, subgroupId); + return new NounMetadata(result, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); + } + + @Override + public String getReactorDescription() { + return "Get all users assigned to a sub-group."; + } +} diff --git a/src/prerna/reactor/appprofile/GetAppSubgroupsReactor.java b/src/prerna/reactor/appprofile/GetAppSubgroupsReactor.java new file mode 100644 index 00000000000..85b008cb166 --- /dev/null +++ b/src/prerna/reactor/appprofile/GetAppSubgroupsReactor.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import java.util.List; +import java.util.Map; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class GetAppSubgroupsReactor extends AbstractReactor { + + private static final Logger classLogger = LogManager.getLogger(GetAppSubgroupsReactor.class); + + public GetAppSubgroupsReactor() { + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.PROFILE_ID.getKey() }; + this.keyRequired = new int[] { 1, 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); + + if (!AppProfileUtils.canManageProfiles(user, appId)) { + throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); + } + List> result = AppProfileUtils.getSubgroups(appId, profileId); + return new NounMetadata(result, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); + } + + @Override + public String getReactorDescription() { + return "Get all sub-groups within a group-style profile."; + } +} diff --git a/src/prerna/reactor/appprofile/GetAppUserFeaturesReactor.java b/src/prerna/reactor/appprofile/GetAppUserFeaturesReactor.java new file mode 100644 index 00000000000..0b1e2b70b69 --- /dev/null +++ b/src/prerna/reactor/appprofile/GetAppUserFeaturesReactor.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import java.util.Map; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class GetAppUserFeaturesReactor extends AbstractReactor { + + private static final Logger classLogger = LogManager.getLogger(GetAppUserFeaturesReactor.class); + + public GetAppUserFeaturesReactor() { + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey() }; + this.keyRequired = new int[] { 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + + if (!AppProfileUtils.canEvaluateFeatures(user, appId)) { + throw new IllegalArgumentException("User does not have access to this app."); + } + Map features = AppProfileUtils.getUserFeatures(appId, user); + return new NounMetadata(features, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); + } + + @Override + public String getReactorDescription() { + return "Get all enabled features for the calling user in an app, unioned across all assigned profiles and subgroups."; + } +} diff --git a/src/prerna/reactor/appprofile/GetUserAppProfileReactor.java b/src/prerna/reactor/appprofile/GetUserAppProfileReactor.java new file mode 100644 index 00000000000..84a1951f2aa --- /dev/null +++ b/src/prerna/reactor/appprofile/GetUserAppProfileReactor.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import java.util.Map; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +/** + * @deprecated Use {@link GetUserAppProfilesReactor} (Pixel: GetUserAppProfiles). Identical behavior. + */ +@Deprecated +public class GetUserAppProfileReactor extends AbstractReactor { + + private static final Logger classLogger = LogManager.getLogger(GetUserAppProfileReactor.class); + + public GetUserAppProfileReactor() { + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey() }; + this.keyRequired = new int[] { 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + + if (!AppProfileUtils.canEvaluateFeatures(user, appId)) { + throw new IllegalArgumentException("User does not have access to this app."); + } + Map result = AppProfileUtils.getUserAppProfiles(appId, user); + return new NounMetadata(result, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); + } + + @Override + public String getReactorDescription() { + return "Alias for GetUserAppProfiles — identical behavior. Get the calling user's profile and subgroup memberships for an app."; + } +} diff --git a/src/prerna/reactor/appprofile/GetUserAppProfilesReactor.java b/src/prerna/reactor/appprofile/GetUserAppProfilesReactor.java new file mode 100644 index 00000000000..c50dbd21096 --- /dev/null +++ b/src/prerna/reactor/appprofile/GetUserAppProfilesReactor.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import java.util.Map; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class GetUserAppProfilesReactor extends AbstractReactor { + + private static final Logger classLogger = LogManager.getLogger(GetUserAppProfilesReactor.class); + + public GetUserAppProfilesReactor() { + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey() }; + this.keyRequired = new int[] { 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + + if (!AppProfileUtils.canEvaluateFeatures(user, appId)) { + throw new IllegalArgumentException("User does not have access to this app."); + } + Map result = AppProfileUtils.getUserAppProfiles(appId, user); + return new NounMetadata(result, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); + } + + @Override + public String getReactorDescription() { + return "Get all profile and subgroup memberships for the calling user in an app."; + } +} diff --git a/src/prerna/reactor/appprofile/GetUserFeaturesReactor.java b/src/prerna/reactor/appprofile/GetUserFeaturesReactor.java index 67e0e9aa3d9..05b174f64a7 100644 --- a/src/prerna/reactor/appprofile/GetUserFeaturesReactor.java +++ b/src/prerna/reactor/appprofile/GetUserFeaturesReactor.java @@ -29,16 +29,26 @@ import java.util.Map; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import prerna.auth.User; import prerna.auth.utils.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * @deprecated Use {@link GetAppUserFeaturesReactor} (Pixel: GetAppUserFeatures). + */ +@Deprecated public class GetUserFeaturesReactor extends AbstractReactor { + private static final Logger classLogger = LogManager.getLogger(GetUserFeaturesReactor.class); + public GetUserFeaturesReactor() { - this.keysToGet = new String[] { "app" }; + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey() }; this.keyRequired = new int[] { 1 }; } @@ -46,14 +56,14 @@ public GetUserFeaturesReactor() { public NounMetadata execute() { organizeKeys(); User user = this.insight.getUser(); - String appId = this.keyValue.get("app"); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); if (!AppProfileUtils.canEvaluateFeatures(user, appId)) { throw new IllegalArgumentException("User does not have access to this app."); } // Returns only enabled features — callers cannot infer what features exist but are hidden Map features = AppProfileUtils.getUserFeatures(appId, user); - return new NounMetadata(features, PixelDataType.MAP); + return new NounMetadata(features, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); } @Override diff --git a/src/prerna/reactor/appprofile/GetUserProfileReactor.java b/src/prerna/reactor/appprofile/GetUserProfileReactor.java index b1141e18182..db6a2ff89e0 100644 --- a/src/prerna/reactor/appprofile/GetUserProfileReactor.java +++ b/src/prerna/reactor/appprofile/GetUserProfileReactor.java @@ -27,18 +27,25 @@ *******************************************************************************/ package prerna.reactor.appprofile; +import java.util.List; import java.util.Map; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import prerna.auth.User; import prerna.auth.utils.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; public class GetUserProfileReactor extends AbstractReactor { + private static final Logger classLogger = LogManager.getLogger(GetUserProfileReactor.class); + public GetUserProfileReactor() { - this.keysToGet = new String[] { "app", "userId" }; + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.USER_ID.getKey() }; this.keyRequired = new int[] { 1, 1 }; } @@ -46,18 +53,18 @@ public GetUserProfileReactor() { public NounMetadata execute() { organizeKeys(); User user = this.insight.getUser(); - String appId = this.keyValue.get("app"); - String userId = this.keyValue.get("userId"); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + String userId = this.keyValue.get(ReactorKeysEnum.USER_ID.getKey()); if (!AppProfileUtils.canManageProfiles(user, appId)) { throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); } - Map profile = AppProfileUtils.getUserProfile(appId, userId); - return new NounMetadata(profile, PixelDataType.MAP); + List> profiles = AppProfileUtils.getUserProfiles(appId, userId); + return new NounMetadata(profiles, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); } @Override public String getReactorDescription() { - return "Get the effective profile for a user in an app."; + return "Get all profile assignments for a specific user in an app. Requires manage permission."; } } diff --git a/src/prerna/reactor/appprofile/RemoveAppProfileManagerReactor.java b/src/prerna/reactor/appprofile/RemoveAppProfileManagerReactor.java new file mode 100644 index 00000000000..e040c95a6b5 --- /dev/null +++ b/src/prerna/reactor/appprofile/RemoveAppProfileManagerReactor.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; + +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class RemoveAppProfileManagerReactor extends AbstractReactor { + + private static final Logger classLogger = LogManager.getLogger(RemoveAppProfileManagerReactor.class); + + public RemoveAppProfileManagerReactor() { + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.USER_ID.getKey() }; + this.keyRequired = new int[] { 1, 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + String userId = this.keyValue.get(ReactorKeysEnum.USER_ID.getKey()); + + if (!AppProfileUtils.canManageProfiles(user, appId)) { + throw new IllegalArgumentException("Only app owners/editors can revoke profile manager permissions."); + } + AppProfileUtils.removeProfileManager(appId, userId); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Profile manager removed.")); + return noun; + } + + @Override + public String getReactorDescription() { + return "Revoke a user's delegated profile manager permission."; + } +} diff --git a/src/prerna/reactor/appprofile/RemoveAppUserProfileReactor.java b/src/prerna/reactor/appprofile/RemoveAppUserProfileReactor.java new file mode 100644 index 00000000000..a2371acefb1 --- /dev/null +++ b/src/prerna/reactor/appprofile/RemoveAppUserProfileReactor.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class RemoveAppUserProfileReactor extends AbstractReactor { + + private static final Logger classLogger = LogManager.getLogger(RemoveAppUserProfileReactor.class); + + public RemoveAppUserProfileReactor() { + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.USER_ID.getKey(), ReactorKeysEnum.PROFILE_ID.getKey() }; + this.keyRequired = new int[] { 1, 1, 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + String userId = this.keyValue.get(ReactorKeysEnum.USER_ID.getKey()); + String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); + + if (!AppProfileUtils.canAssignProfiles(user, appId)) { + throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); + } + AppProfileUtils.removeUserProfile(appId, userId, profileId); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("User removed from profile.")); + return noun; + } + + @Override + public String getReactorDescription() { + return "Remove a user from a specific profile assignment for an app."; + } +} diff --git a/src/prerna/reactor/appprofile/RemoveAppUserSubgroupReactor.java b/src/prerna/reactor/appprofile/RemoveAppUserSubgroupReactor.java new file mode 100644 index 00000000000..d2ab0053786 --- /dev/null +++ b/src/prerna/reactor/appprofile/RemoveAppUserSubgroupReactor.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class RemoveAppUserSubgroupReactor extends AbstractReactor { + + private static final Logger classLogger = LogManager.getLogger(RemoveAppUserSubgroupReactor.class); + + public RemoveAppUserSubgroupReactor() { + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.USER_ID.getKey(), ReactorKeysEnum.SUBGROUP_ID.getKey() }; + this.keyRequired = new int[] { 1, 1, 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + String userId = this.keyValue.get(ReactorKeysEnum.USER_ID.getKey()); + String subgroupId = this.keyValue.get(ReactorKeysEnum.SUBGROUP_ID.getKey()); + + if (!AppProfileUtils.canAssignProfiles(user, appId)) { + throw new IllegalArgumentException("User does not have permission to manage users for this app."); + } + AppProfileUtils.removeUserSubgroup(appId, userId, subgroupId); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("User removed from subgroup.")); + return noun; + } + + @Override + public String getReactorDescription() { + return "Remove a user from a sub-group."; + } +} diff --git a/src/prerna/reactor/appprofile/RemoveUserProfileReactor.java b/src/prerna/reactor/appprofile/RemoveUserProfileReactor.java index 6f91e41329e..9ed991f0422 100644 --- a/src/prerna/reactor/appprofile/RemoveUserProfileReactor.java +++ b/src/prerna/reactor/appprofile/RemoveUserProfileReactor.java @@ -27,16 +27,29 @@ *******************************************************************************/ package prerna.reactor.appprofile; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import prerna.auth.User; import prerna.auth.utils.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * @deprecated Use {@link RemoveAppUserProfileReactor} (Pixel: RemoveAppUserProfile) to remove a user from a specific profile. + * WARNING: This reactor removes the user from ALL profiles for the app, not just one. + * The new RemoveAppUserProfile reactor requires a profileId and removes only that assignment. + * Kept for backwards compatibility. + */ +@Deprecated public class RemoveUserProfileReactor extends AbstractReactor { + private static final Logger classLogger = LogManager.getLogger(RemoveUserProfileReactor.class); + public RemoveUserProfileReactor() { - this.keysToGet = new String[] { "app", "userId" }; + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.USER_ID.getKey() }; this.keyRequired = new int[] { 1, 1 }; } @@ -44,20 +57,20 @@ public RemoveUserProfileReactor() { public NounMetadata execute() { organizeKeys(); User user = this.insight.getUser(); - String appId = this.keyValue.get("app"); - String userId = this.keyValue.get("userId"); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + String userId = this.keyValue.get(ReactorKeysEnum.USER_ID.getKey()); - if (!AppProfileUtils.canManageProfiles(user, appId)) { + if (!AppProfileUtils.canAssignProfiles(user, appId)) { throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); } AppProfileUtils.removeUserProfile(appId, userId); - NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN); - noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("User removed from profile.")); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("User removed from all profiles.")); return noun; } @Override public String getReactorDescription() { - return "Remove a user's profile assignment for an app."; + return "Remove a user from all profile assignments for an app."; } } diff --git a/src/prerna/reactor/appprofile/SetProfileFeatureReactor.java b/src/prerna/reactor/appprofile/SetAppProfileFeatureReactor.java similarity index 72% rename from src/prerna/reactor/appprofile/SetProfileFeatureReactor.java rename to src/prerna/reactor/appprofile/SetAppProfileFeatureReactor.java index acdddc80293..dbf5236dcc7 100644 --- a/src/prerna/reactor/appprofile/SetProfileFeatureReactor.java +++ b/src/prerna/reactor/appprofile/SetAppProfileFeatureReactor.java @@ -27,16 +27,23 @@ *******************************************************************************/ package prerna.reactor.appprofile; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; + import prerna.auth.User; import prerna.auth.utils.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.nounmeta.NounMetadata; -public class SetProfileFeatureReactor extends AbstractReactor { +public class SetAppProfileFeatureReactor extends AbstractReactor { + + private static final Logger classLogger = LogManager.getLogger(SetAppProfileFeatureReactor.class); - public SetProfileFeatureReactor() { - this.keysToGet = new String[] { "app", "profileId", "featureId", "enabled" }; + public SetAppProfileFeatureReactor() { + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.PROFILE_ID.getKey(), ReactorKeysEnum.FEATURE_ID.getKey(), ReactorKeysEnum.ENABLED.getKey() }; this.keyRequired = new int[] { 1, 1, 1, 1 }; } @@ -44,16 +51,16 @@ public SetProfileFeatureReactor() { public NounMetadata execute() { organizeKeys(); User user = this.insight.getUser(); - String appId = this.keyValue.get("app"); - String profileId = this.keyValue.get("profileId"); - String featureId = this.keyValue.get("featureId"); - boolean enabled = Boolean.parseBoolean(this.keyValue.get("enabled")); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); + String featureId = this.keyValue.get(ReactorKeysEnum.FEATURE_ID.getKey()); + boolean enabled = Boolean.parseBoolean(this.keyValue.get(ReactorKeysEnum.ENABLED.getKey())); if (!AppProfileUtils.canManageProfiles(user, appId)) { throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); } AppProfileUtils.setProfileFeature(appId, profileId, featureId, enabled, user); - NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Profile feature updated.")); return noun; } diff --git a/src/prerna/reactor/appprofile/SetAppSubgroupFeatureReactor.java b/src/prerna/reactor/appprofile/SetAppSubgroupFeatureReactor.java new file mode 100644 index 00000000000..9584a4287a6 --- /dev/null +++ b/src/prerna/reactor/appprofile/SetAppSubgroupFeatureReactor.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; + +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class SetAppSubgroupFeatureReactor extends AbstractReactor { + + private static final Logger classLogger = LogManager.getLogger(SetAppSubgroupFeatureReactor.class); + + public SetAppSubgroupFeatureReactor() { + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.SUBGROUP_ID.getKey(), ReactorKeysEnum.FEATURE_ID.getKey(), ReactorKeysEnum.ENABLED.getKey() }; + this.keyRequired = new int[] { 1, 1, 1, 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + String subgroupId = this.keyValue.get(ReactorKeysEnum.SUBGROUP_ID.getKey()); + String featureId = this.keyValue.get(ReactorKeysEnum.FEATURE_ID.getKey()); + boolean enabled = Boolean.parseBoolean(this.keyValue.get(ReactorKeysEnum.ENABLED.getKey())); + + if (!AppProfileUtils.canManageProfiles(user, appId)) { + throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); + } + AppProfileUtils.setSubgroupFeature(appId, subgroupId, featureId, enabled, user); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Subgroup feature updated.")); + return noun; + } + + @Override + public String getReactorDescription() { + return "Enable or disable a feature for a sub-group."; + } +} diff --git a/src/prerna/reactor/appprofile/UpdateAppFeatureReactor.java b/src/prerna/reactor/appprofile/UpdateAppFeatureReactor.java index 2a1a78cc3b1..9acc501334f 100644 --- a/src/prerna/reactor/appprofile/UpdateAppFeatureReactor.java +++ b/src/prerna/reactor/appprofile/UpdateAppFeatureReactor.java @@ -27,16 +27,22 @@ *******************************************************************************/ package prerna.reactor.appprofile; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import prerna.auth.User; import prerna.auth.utils.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; public class UpdateAppFeatureReactor extends AbstractReactor { + private static final Logger classLogger = LogManager.getLogger(UpdateAppFeatureReactor.class); + public UpdateAppFeatureReactor() { - this.keysToGet = new String[] { "app", "featureId", "key", "description" }; + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.FEATURE_ID.getKey(), ReactorKeysEnum.FEATURE_KEY.getKey(), ReactorKeysEnum.DESCRIPTION.getKey() }; this.keyRequired = new int[] { 1, 1, 0, 0 }; } @@ -44,16 +50,16 @@ public UpdateAppFeatureReactor() { public NounMetadata execute() { organizeKeys(); User user = this.insight.getUser(); - String appId = this.keyValue.get("app"); - String featureId = this.keyValue.get("featureId"); - String featureKey = this.keyValue.get("key"); - String description = this.keyValue.get("description"); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + String featureId = this.keyValue.get(ReactorKeysEnum.FEATURE_ID.getKey()); + String featureKey = this.keyValue.get(ReactorKeysEnum.FEATURE_KEY.getKey()); + String description = this.keyValue.get(ReactorKeysEnum.DESCRIPTION.getKey()); if (!AppProfileUtils.canManageProfiles(user, appId)) { throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); } AppProfileUtils.updateFeature(appId, featureId, featureKey, description, user); - NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Feature updated.")); return noun; } diff --git a/src/prerna/reactor/appprofile/UpdateAppProfileReactor.java b/src/prerna/reactor/appprofile/UpdateAppProfileReactor.java index 994a1926751..2fb2d2e2715 100644 --- a/src/prerna/reactor/appprofile/UpdateAppProfileReactor.java +++ b/src/prerna/reactor/appprofile/UpdateAppProfileReactor.java @@ -27,35 +27,43 @@ *******************************************************************************/ package prerna.reactor.appprofile; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import prerna.auth.User; import prerna.auth.utils.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; public class UpdateAppProfileReactor extends AbstractReactor { + private static final Logger classLogger = LogManager.getLogger(UpdateAppProfileReactor.class); + public UpdateAppProfileReactor() { - this.keysToGet = new String[] { "app", "profileId", "name", "description", "isDefault" }; - this.keyRequired = new int[] { 1, 1, 0, 0, 0 }; + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.PROFILE_ID.getKey(), ReactorKeysEnum.NAME.getKey(), ReactorKeysEnum.DESCRIPTION.getKey(), ReactorKeysEnum.IS_DEFAULT.getKey(), ReactorKeysEnum.IS_GROUP.getKey() }; + this.keyRequired = new int[] { 1, 1, 0, 0, 0, 0 }; } @Override public NounMetadata execute() { organizeKeys(); User user = this.insight.getUser(); - String appId = this.keyValue.get("app"); - String profileId = this.keyValue.get("profileId"); - String name = this.keyValue.get("name"); - String description = this.keyValue.get("description"); - String isDefaultStr = this.keyValue.get("isDefault"); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); + String name = this.keyValue.get(ReactorKeysEnum.NAME.getKey()); + String description = this.keyValue.get(ReactorKeysEnum.DESCRIPTION.getKey()); + String isDefaultStr = this.keyValue.get(ReactorKeysEnum.IS_DEFAULT.getKey()); Boolean isDefault = isDefaultStr != null ? Boolean.parseBoolean(isDefaultStr) : null; + String isGroupStr = this.keyValue.get(ReactorKeysEnum.IS_GROUP.getKey()); + Boolean isGroup = isGroupStr != null ? Boolean.parseBoolean(isGroupStr) : null; if (!AppProfileUtils.canManageProfiles(user, appId)) { throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); } - AppProfileUtils.updateProfile(appId, profileId, name, description, isDefault, user); - NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN); + AppProfileUtils.updateProfile(appId, profileId, name, description, isDefault, isGroup, user); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Profile updated.")); return noun; } diff --git a/src/prerna/reactor/appprofile/UpdateAppSubgroupReactor.java b/src/prerna/reactor/appprofile/UpdateAppSubgroupReactor.java new file mode 100644 index 00000000000..7b58d9e8909 --- /dev/null +++ b/src/prerna/reactor/appprofile/UpdateAppSubgroupReactor.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright 2015 Defense Health Agency (DHA) + * + * If your use of this software does not include any GPLv2 components: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---------------------------------------------------------------------------- + * If your use of this software includes any GPLv2 components: + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *******************************************************************************/ +package prerna.reactor.appprofile; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import prerna.auth.User; +import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; +import prerna.sablecc2.om.nounmeta.NounMetadata; + +public class UpdateAppSubgroupReactor extends AbstractReactor { + + private static final Logger classLogger = LogManager.getLogger(UpdateAppSubgroupReactor.class); + + public UpdateAppSubgroupReactor() { + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.SUBGROUP_ID.getKey(), ReactorKeysEnum.NAME.getKey(), ReactorKeysEnum.DESCRIPTION.getKey() }; + this.keyRequired = new int[] { 1, 1, 0, 0 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); + String subgroupId = this.keyValue.get(ReactorKeysEnum.SUBGROUP_ID.getKey()); + String name = this.keyValue.get(ReactorKeysEnum.NAME.getKey()); + String description = this.keyValue.get(ReactorKeysEnum.DESCRIPTION.getKey()); + + if (!AppProfileUtils.canManageProfiles(user, appId)) { + throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); + } + AppProfileUtils.updateSubgroup(appId, subgroupId, name, description, user); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Subgroup updated.")); + return noun; + } + + @Override + public String getReactorDescription() { + return "Update a sub-group's name or description."; + } +} diff --git a/src/prerna/sablecc2/om/ReactorKeysEnum.java b/src/prerna/sablecc2/om/ReactorKeysEnum.java index 726e9a71b0e..84ac795e0df 100644 --- a/src/prerna/sablecc2/om/ReactorKeysEnum.java +++ b/src/prerna/sablecc2/om/ReactorKeysEnum.java @@ -40,7 +40,7 @@ public enum ReactorKeysEnum { ALL("all", "Boolean to use all the values"), ALL_NUMERIC_KEY("allNumeric", "Indicates if only numeric headers should be returned"), ALIAS("alias", "An alias to assign for an operation or output - use .as([\"aliasName\"])"), -// APP("app", "Name of the app on the local SEMOSS instance"), + APP("app", "The id of the app/project"), ANIMATE("animate", "Specifies if the ggplot needs to be animated"), API_KEY("apikey", "API key being used for the specific insight / project"), ARRAY("array", "An array of input values"), @@ -89,6 +89,7 @@ public enum ReactorKeysEnum { DRY_RUN("dryRun", "Boolean whether to perform a dry run of the operation without impacting application state (default is true)"), EMAIL_SESSION("emailSession", "The javax.mail email session object"), ENABLE("enable", "Boolean whether to enable"), + ENABLED("enabled", "Boolean whether a feature or item is enabled"), EVENTS_KEY("events", "Events map input"), EXISTING("existing", "Add to exisitng app"), END("end", "Ending value for a between reactor"), @@ -107,6 +108,8 @@ public enum ReactorKeysEnum { FILE_NAME("fileName", "Name of the file"), FILE_PATH("filePath", "Relative file path location"), FILTERS("filters", "Filters automatically persisted on queries affecting this frame or panel"), + FEATURE_ID("featureId", "The id of a feature within an app profile"), + FEATURE_KEY("featureKey", "The string key identifier for an app feature"), FILTER_WORD("filterWord", "Regex to apply for searches"), FORMAT("format", "The format to save the information as Jpeg, Gif, PNG"), FOOTER("footer", "Footer for the export template if there is any"), @@ -140,6 +143,8 @@ public enum ReactorKeysEnum { ID("id", "This key can represent the unique id of the insight instance or the unique id of the saved insight relative to the app"), ID_TYPE("id_type", "Type of the id for setting param. colum / column_table / column_table_operator"), IMAGE("image", "The location of the image file or the encoding of the image as a png"), + IS_DEFAULT("isDefault", "Whether this profile is the default/fallback profile for the app"), + IS_GROUP("isGroup", "Whether this profile is a group-style profile containing sub-groups"), IMAGE_WAIT_TIME("imageWaitTime", "Time in ms to wait for the image to be generated on the BE before operations like screenshots"), INCLUDE_META_KEY("meta", "Boolean indication (true or false) of whether to retrieve metadata"), INCLUDE_USERTRACKING_KEY("userT", "Boolean indication (true or false) of whether to retrieve user tracking metrics"), @@ -154,7 +159,7 @@ public enum ReactorKeysEnum { JOB_TAGS("jobTags", "List of job tags to use for filtering"), JOINS("joins", "Joins on the frame"), JSON("json", "JSON that is the equivalent of a map for key-value properties"), - JSON_CLEANUP("jsonCleanup", "Legacy compatibility flag to decode older escaped JSON string payloads; modern clients should not set this."), + JSON_CLEANUP("jsonCleanup", "Legacy compatibility flag to decode older escaped JSON string payloads; modern clients should not set this."), LAYER("layer", "The id for the layer of this visualization"), LAMBDA("lambda", "Name of the lambda transformtion to perform"), LANGUAGE("language", "Language in which this expression needs to be interpreted"), @@ -234,8 +239,9 @@ public enum ReactorKeysEnum { PDF_SIGNATURE_BLOCK("pdfSignatureBlock", "Boolean to add digital signature block in exisitng pdf file"), PDF_SIGNATURE_LABEL("pdfSignatureLabel", "String containing text to add above the signature block to specify signature label"), PERMISSION("permission", "Permission level"), + PROFILE_ID("profileId", "The id of the app profile"), PERMISSION_FILTERS("permissionFilters", "Additional filter to append based on permission level (1=Owner, 2=Editor, 3=ReadOnly)"), - PINNED("pinned", "Optional boolean filter; true to return only pinned items, false to return only non-pinned items, omit for no filter."), + PINNED("pinned", "Optional boolean filter; true to return only pinned items, false to return only non-pinned items, omit for no filter."), PIXEL("pixel", "Pixel script as string"), PIXEL_ID("pixelId", "The pixel id for this pixel step"), PLACE_HOLDER_DATA("placeHolderData", "Updated place holder information of the template"), @@ -275,7 +281,7 @@ public enum ReactorKeysEnum { ROW_GUTTER("rowgutter", "Number of rows to pad between subsequent tables"), RULES_MAP("rulesMap", "The map of rules for validation, including information such as the name of the rule, the rule definition, the columns, and the description"), SCALE("scale", "How much to scale the graph, default value is set at 20 based on screen size"), - SCHEMA("schema", "The database schema."), + SCHEMA("schema", "The database schema."), SEARCH("search", "The search term."), SEARCH_RESULTS("searchResults", "The result list from a search"), SECTION("section", "The section to use as input from addBlockReactor"), @@ -305,6 +311,7 @@ When this parameter is not provided, the space is assumed to be the current insi SUBTOTALS("subtotals", "All the columns in a pivot that you need subtotal for. Default is all. "), SUM_RANGE("sumRange", "Range that values to sum over"), SUB_TYPE("subType", "Sub Type of each type Project or Engine"), + SUBGROUP_ID("subgroupId", "The id of a sub-group within a group-style app profile"), SYNC_PULL("dual", "True/False value to determine if the sync should also pull the latest updates from the repository"), SYNC_DATABASE("syncDatabase", "True/False value to detetermine if the database should be published with the app"), SPLOT("splot", "Seaborn plot expression"), @@ -325,6 +332,7 @@ When this parameter is not provided, the space is assumed to be the current insi TYPE("type", "Type Project or Engine"), UNIQUE_COLUMN("uniqueColumn", "Unique column identifier for csv/excel table uploads"), USE_FRAME_FILTERS("useFrameFilters", "A boolean indication (true or false) to use frame filters"), + USER_ID("userId", "The id of a user"), USERNAME("username", "Unique identifier for the user to access a service"), QUERY_KEY("query", "Query string to be executed on the database"), QUERY_STRUCT("qs", "QueryStruct object that contains selectors, filters, and joins"), From 888d3e7d2da8cbdf4ac688d5b287c98a503bb69e Mon Sep 17 00:00:00 2001 From: "Patel, Parth" Date: Thu, 25 Jun 2026 15:49:26 -0400 Subject: [PATCH 5/9] fix: group profile base features propagate to subgroup members; expand read access to profile managers - AppProfileUtils: `getExplicitUserSubgroups` now returns PROFILE_ID so the parent group profile can be looked up for each subgroup membership - AppProfileUtils: `checkFeature` now unions subgroup features with the parent group profile's base features, so group-level toggles apply to all members - AppProfileUtils: `getUserFeatures` does the same union for the full feature list returned to the client - 8 read-only reactors (GetAppFeatures, GetAppProfileFeatures, GetAppProfileManagers, GetAppProfileUsers, GetAppProfiles, GetAppSubgroupFeatures, GetAppSubgroups, GetUserProfile) changed from `canManageProfiles` to `canAssignProfiles` so profile managers can view profiles and assign users without needing full admin - PlatformProfileUtils: import cleanup and remove deprecated nav.build from predefined feature keys Co-Authored-By: Claude Sonnet 4.6 --- src/prerna/auth/utils/AppProfileUtils.java | 20 ++- .../auth/utils/PlatformProfileUtils.java | 161 ++++++++++-------- .../appprofile/GetAppFeaturesReactor.java | 2 +- .../GetAppProfileFeaturesReactor.java | 2 +- .../GetAppProfileManagersReactor.java | 2 +- .../appprofile/GetAppProfileUsersReactor.java | 2 +- .../appprofile/GetAppProfilesReactor.java | 2 +- .../GetAppSubgroupFeaturesReactor.java | 2 +- .../appprofile/GetAppSubgroupsReactor.java | 2 +- .../appprofile/GetUserProfileReactor.java | 2 +- 10 files changed, 112 insertions(+), 85 deletions(-) diff --git a/src/prerna/auth/utils/AppProfileUtils.java b/src/prerna/auth/utils/AppProfileUtils.java index c3a19e763a3..0dab329b469 100644 --- a/src/prerna/auth/utils/AppProfileUtils.java +++ b/src/prerna/auth/utils/AppProfileUtils.java @@ -1111,12 +1111,17 @@ public static boolean checkFeature(String appId, String featureKey, User user) { } } - // Check across all subgroup memberships + // Check across all subgroup memberships, plus the parent group profile's base features List> subgroups = getExplicitUserSubgroups(securityDb, appId, userId); for (Map sg : subgroups) { if (querySubgroupFeatureEnabled(securityDb, appId, (String) sg.get("subgroupId"), featureId)) { return true; } + // Group profile base features apply to all subgroup members + String parentProfileId = (String) sg.get("profileId"); + if (parentProfileId != null && queryFeatureEnabled(securityDb, appId, parentProfileId, featureId)) { + return true; + } } return false; @@ -1152,7 +1157,7 @@ public static Map getUserFeatures(String appId, User user) { } } - // Subgroup features + // Subgroup features + parent group profile base features List> subgroups = getExplicitUserSubgroups(securityDb, appId, userId); for (Map sg : subgroups) { addSubgroupFeaturesToResult(securityDb, appId, @@ -1160,6 +1165,14 @@ public static Map getUserFeatures(String appId, User user) { (String) sg.get("subgroupName"), (String) sg.get("profileName"), result); + // Group profile base features apply to all subgroup members + String parentProfileId = (String) sg.get("profileId"); + if (parentProfileId != null) { + addProfileFeaturesToResult(securityDb, appId, + parentProfileId, + (String) sg.get("profileName"), + false, result); + } } return result; @@ -1294,7 +1307,7 @@ private static List> getExplicitUserProfiles(IRDBMSEngine se private static List> getExplicitUserSubgroups(IRDBMSEngine securityDb, String appId, String userId) { List> subgroups = new ArrayList<>(); - String sql = "SELECT us.SUBGROUP_ID, sg.SUBGROUP_NAME, p.PROFILE_NAME " + String sql = "SELECT us.SUBGROUP_ID, sg.SUBGROUP_NAME, p.PROFILE_ID, p.PROFILE_NAME " + "FROM APP_USER_SUBGROUP us " + "JOIN APP_PROFILE_SUBGROUP sg ON us.SUBGROUP_ID = sg.SUBGROUP_ID " + "JOIN APP_PROFILE p ON sg.PROFILE_ID = p.PROFILE_ID " @@ -1310,6 +1323,7 @@ private static List> getExplicitUserSubgroups(IRDBMSEngine s Map row = new HashMap<>(); row.put("subgroupId", rs.getString("SUBGROUP_ID")); row.put("subgroupName", rs.getString("SUBGROUP_NAME")); + row.put("profileId", rs.getString("PROFILE_ID")); row.put("profileName", rs.getString("PROFILE_NAME")); subgroups.add(row); } diff --git a/src/prerna/auth/utils/PlatformProfileUtils.java b/src/prerna/auth/utils/PlatformProfileUtils.java index 6d941319a0f..00745e8d258 100644 --- a/src/prerna/auth/utils/PlatformProfileUtils.java +++ b/src/prerna/auth/utils/PlatformProfileUtils.java @@ -28,15 +28,14 @@ package prerna.auth.utils; import java.sql.PreparedStatement; -import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; -import java.util.ArrayList; import java.util.Map; import java.util.Set; import java.util.UUID; @@ -46,6 +45,14 @@ import prerna.auth.User; import prerna.engine.api.IRDBMSEngine; +import prerna.engine.api.IRawSelectWrapper; +import prerna.query.querystruct.SelectQueryStruct; +import prerna.query.querystruct.filters.SimpleQueryFilter; +import prerna.query.querystruct.selectors.QueryColumnOrderBySelector; +import prerna.query.querystruct.selectors.QueryColumnSelector; +import prerna.query.querystruct.selectors.QueryFunctionHelper; +import prerna.query.querystruct.selectors.QueryFunctionSelector; +import prerna.rdf.engine.wrappers.WrapperManager; import prerna.util.ConnectionUtils; import prerna.util.SystemEngineRegistry; import prerna.util.Utility; @@ -57,7 +64,6 @@ public class PlatformProfileUtils { public static final Set PREDEFINED_FEATURE_KEYS = Collections.unmodifiableSet( new HashSet<>(Arrays.asList( "nav.app-catalog", - "nav.build", "nav.skills", "nav.settings", "nav.engine"))); @@ -169,28 +175,30 @@ public static void deleteProfile(String profileId, User user) { public static List> getProfiles(User user) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); List> profiles = new ArrayList<>(); - String sql = "SELECT PROFILE_ID, PROFILE_NAME, DESCRIPTION, CREATED_BY, CREATED_AT " - + "FROM PLATFORM_PROFILE ORDER BY PROFILE_NAME"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(sql); - rs = ps.executeQuery(); - while (rs.next()) { + + SelectQueryStruct qs = new SelectQueryStruct(); + qs.addSelector(new QueryColumnSelector("PLATFORM_PROFILE__PROFILE_ID")); + qs.addSelector(new QueryColumnSelector("PLATFORM_PROFILE__PROFILE_NAME")); + qs.addSelector(new QueryColumnSelector("PLATFORM_PROFILE__DESCRIPTION")); + qs.addSelector(new QueryColumnSelector("PLATFORM_PROFILE__CREATED_BY")); + qs.addSelector(new QueryColumnSelector("PLATFORM_PROFILE__CREATED_AT")); + qs.addOrderBy(new QueryColumnOrderBySelector("PLATFORM_PROFILE__PROFILE_NAME")); + + try (IRawSelectWrapper wrapper = WrapperManager.getInstance().getRawWrapper(securityDb, qs)) { + while (wrapper.hasNext()) { + Object[] row = wrapper.next().getValues(); + String pid = (String) row[0]; Map profile = new HashMap<>(); - String pid = rs.getString("PROFILE_ID"); profile.put("profileId", pid); - profile.put("profileName", rs.getString("PROFILE_NAME")); - profile.put("description", rs.getString("DESCRIPTION")); - profile.put("createdBy", rs.getString("CREATED_BY")); - profile.put("createdAt", rs.getTimestamp("CREATED_AT")); + profile.put("profileName", row[1]); + profile.put("description", row[2]); + profile.put("createdBy", row[3]); + profile.put("createdAt", row[4]); profile.put("userCount", getAssignedUserCount(securityDb, pid)); profiles.add(profile); } - } catch (SQLException e) { + } catch (Exception e) { classLogger.error("Failed to get platform profiles", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); } return profiles; } @@ -201,7 +209,7 @@ public static void setProfileFeature(String profileId, String featureKey, boolea if (!PREDEFINED_FEATURE_KEYS.contains(featureKey)) { throw new IllegalArgumentException( "Unknown platform feature key: " + featureKey - + ". Valid keys: " + PREDEFINED_FEATURE_KEYS); + + ". Valid keys: " + new java.util.TreeSet<>(PREDEFINED_FEATURE_KEYS)); } IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); PreparedStatement ps = null; @@ -236,28 +244,29 @@ public static void setProfileFeature(String profileId, String featureKey, boolea public static Map getProfileFeatures(String profileId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - // Start with all keys disabled Map result = new LinkedHashMap<>(); for (String key : PREDEFINED_FEATURE_KEYS) { result.put(key, Boolean.FALSE); } - String sql = "SELECT FEATURE_KEY, ENABLED FROM PLATFORM_PROFILE_FEATURE WHERE PROFILE_ID=?"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(sql); - ps.setString(1, profileId); - rs = ps.executeQuery(); - while (rs.next()) { - String key = rs.getString("FEATURE_KEY"); - if (PREDEFINED_FEATURE_KEYS.contains(key)) { - result.put(key, rs.getBoolean("ENABLED")); + + SelectQueryStruct qs = new SelectQueryStruct(); + qs.addSelector(new QueryColumnSelector("PLATFORM_PROFILE_FEATURE__FEATURE_KEY")); + qs.addSelector(new QueryColumnSelector("PLATFORM_PROFILE_FEATURE__ENABLED")); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("PLATFORM_PROFILE_FEATURE__PROFILE_ID", "==", profileId)); + + try (IRawSelectWrapper wrapper = WrapperManager.getInstance().getRawWrapper(securityDb, qs)) { + while (wrapper.hasNext()) { + Object[] row = wrapper.next().getValues(); + String key = (String) row[0]; + if (key != null && PREDEFINED_FEATURE_KEYS.contains(key)) { + Object enabledVal = row[1]; + boolean enabled = enabledVal instanceof Boolean ? (Boolean) enabledVal + : (enabledVal != null && "true".equalsIgnoreCase(enabledVal.toString())); + result.put(key, enabled); } } - } catch (SQLException e) { + } catch (Exception e) { classLogger.error("Failed to get platform profile features", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); } return result; } @@ -323,7 +332,6 @@ public static Map getUserFeatures(User user) { String userId = AppProfileUtils.getUserId(user); String profileId = getAssignedProfileId(securityDb, userId); if (profileId == null) { - // No profile assigned → all nav visible Map all = new LinkedHashMap<>(); for (String key : PREDEFINED_FEATURE_KEYS) { all.put(key, Boolean.TRUE); @@ -336,24 +344,29 @@ public static Map getUserFeatures(User user) { public static List> getPlatformProfileUsers(String profileId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); List> users = new ArrayList<>(); - String sql = "SELECT USER_ID, ASSIGNED_BY, ASSIGNED_AT FROM PLATFORM_USER_PROFILE WHERE PROFILE_ID=?"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(sql); - ps.setString(1, profileId); - rs = ps.executeQuery(); - while (rs.next()) { - Map row = new HashMap<>(); - row.put("userId", rs.getString("USER_ID")); - row.put("assignedBy", rs.getString("ASSIGNED_BY")); - row.put("assignedAt", rs.getTimestamp("ASSIGNED_AT")); - users.add(row); + + SelectQueryStruct qs = new SelectQueryStruct(); + qs.addSelector(new QueryColumnSelector("PLATFORM_USER_PROFILE__USER_ID")); + qs.addSelector(new QueryColumnSelector("SMSS_USER__NAME")); + qs.addSelector(new QueryColumnSelector("SMSS_USER__EMAIL")); + qs.addSelector(new QueryColumnSelector("PLATFORM_USER_PROFILE__ASSIGNED_BY")); + qs.addSelector(new QueryColumnSelector("PLATFORM_USER_PROFILE__ASSIGNED_AT")); + qs.addRelation("PLATFORM_USER_PROFILE__USER_ID", "SMSS_USER__ID", "left.join"); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("PLATFORM_USER_PROFILE__PROFILE_ID", "==", profileId)); + + try (IRawSelectWrapper wrapper = WrapperManager.getInstance().getRawWrapper(securityDb, qs)) { + while (wrapper.hasNext()) { + Object[] row = wrapper.next().getValues(); + Map user = new HashMap<>(); + user.put("userId", row[0]); + user.put("name", row[1]); + user.put("email", row[2]); + user.put("assignedBy", row[3]); + user.put("assignedAt", row[4]); + users.add(user); } - } catch (SQLException e) { + } catch (Exception e) { classLogger.error("Failed to get platform profile users", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); } return users; } @@ -361,35 +374,35 @@ public static List> getPlatformProfileUsers(String profileId // ─── Private helpers ───────────────────────────────────────────────────── private static int getAssignedUserCount(IRDBMSEngine securityDb, String profileId) { - String sql = "SELECT COUNT(*) FROM PLATFORM_USER_PROFILE WHERE PROFILE_ID=?"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(sql); - ps.setString(1, profileId); - rs = ps.executeQuery(); - if (rs.next()) return rs.getInt(1); - } catch (SQLException e) { + SelectQueryStruct qs = new SelectQueryStruct(); + QueryFunctionSelector countFn = new QueryFunctionSelector(); + countFn.addInnerSelector(new QueryColumnSelector("PLATFORM_USER_PROFILE__USER_ID")); + countFn.setFunction(QueryFunctionHelper.COUNT); + qs.addSelector(countFn); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("PLATFORM_USER_PROFILE__PROFILE_ID", "==", profileId)); + + try (IRawSelectWrapper wrapper = WrapperManager.getInstance().getRawWrapper(securityDb, qs)) { + if (wrapper.hasNext()) { + Object val = wrapper.next().getValues()[0]; + return val instanceof Number ? ((Number) val).intValue() : 0; + } + } catch (Exception e) { classLogger.error("Failed to count platform profile users", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); } return 0; } private static String getAssignedProfileId(IRDBMSEngine securityDb, String userId) { - String sql = "SELECT PROFILE_ID FROM PLATFORM_USER_PROFILE WHERE USER_ID=?"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(sql); - ps.setString(1, userId); - rs = ps.executeQuery(); - if (rs.next()) return rs.getString("PROFILE_ID"); - } catch (SQLException e) { + SelectQueryStruct qs = new SelectQueryStruct(); + qs.addSelector(new QueryColumnSelector("PLATFORM_USER_PROFILE__PROFILE_ID")); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("PLATFORM_USER_PROFILE__USER_ID", "==", userId)); + + try (IRawSelectWrapper wrapper = WrapperManager.getInstance().getRawWrapper(securityDb, qs)) { + if (wrapper.hasNext()) { + return (String) wrapper.next().getValues()[0]; + } + } catch (Exception e) { classLogger.error("Failed to get platform user profile", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); } return null; } diff --git a/src/prerna/reactor/appprofile/GetAppFeaturesReactor.java b/src/prerna/reactor/appprofile/GetAppFeaturesReactor.java index 2210759e113..b425ea1a0a5 100644 --- a/src/prerna/reactor/appprofile/GetAppFeaturesReactor.java +++ b/src/prerna/reactor/appprofile/GetAppFeaturesReactor.java @@ -55,7 +55,7 @@ public NounMetadata execute() { User user = this.insight.getUser(); String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); - if (!AppProfileUtils.canManageProfiles(user, appId)) { + if (!AppProfileUtils.canAssignProfiles(user, appId)) { throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); } List> features = AppProfileUtils.getFeatures(appId); diff --git a/src/prerna/reactor/appprofile/GetAppProfileFeaturesReactor.java b/src/prerna/reactor/appprofile/GetAppProfileFeaturesReactor.java index 8963d300438..a6726b7373c 100644 --- a/src/prerna/reactor/appprofile/GetAppProfileFeaturesReactor.java +++ b/src/prerna/reactor/appprofile/GetAppProfileFeaturesReactor.java @@ -57,7 +57,7 @@ public NounMetadata execute() { String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); - if (!AppProfileUtils.canManageProfiles(user, appId)) { + if (!AppProfileUtils.canAssignProfiles(user, appId)) { throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); } List> features = AppProfileUtils.getProfileFeatures(appId, profileId); diff --git a/src/prerna/reactor/appprofile/GetAppProfileManagersReactor.java b/src/prerna/reactor/appprofile/GetAppProfileManagersReactor.java index 7d7d6eacaca..cdae7a670b9 100644 --- a/src/prerna/reactor/appprofile/GetAppProfileManagersReactor.java +++ b/src/prerna/reactor/appprofile/GetAppProfileManagersReactor.java @@ -56,7 +56,7 @@ public NounMetadata execute() { User user = this.insight.getUser(); String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); - if (!AppProfileUtils.canManageProfiles(user, appId)) { + if (!AppProfileUtils.canAssignProfiles(user, appId)) { throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); } List> result = AppProfileUtils.getProfileManagers(appId); diff --git a/src/prerna/reactor/appprofile/GetAppProfileUsersReactor.java b/src/prerna/reactor/appprofile/GetAppProfileUsersReactor.java index 50c16e2686a..dde557a3046 100644 --- a/src/prerna/reactor/appprofile/GetAppProfileUsersReactor.java +++ b/src/prerna/reactor/appprofile/GetAppProfileUsersReactor.java @@ -57,7 +57,7 @@ public NounMetadata execute() { String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); - if (!AppProfileUtils.canManageProfiles(user, appId)) { + if (!AppProfileUtils.canAssignProfiles(user, appId)) { throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); } List> users = AppProfileUtils.getProfileUsers(appId, profileId); diff --git a/src/prerna/reactor/appprofile/GetAppProfilesReactor.java b/src/prerna/reactor/appprofile/GetAppProfilesReactor.java index 6cbea525da5..af04372faa2 100644 --- a/src/prerna/reactor/appprofile/GetAppProfilesReactor.java +++ b/src/prerna/reactor/appprofile/GetAppProfilesReactor.java @@ -55,7 +55,7 @@ public NounMetadata execute() { User user = this.insight.getUser(); String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); - if (!AppProfileUtils.canManageProfiles(user, appId)) { + if (!AppProfileUtils.canAssignProfiles(user, appId)) { throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); } List> profiles = AppProfileUtils.getProfiles(appId); diff --git a/src/prerna/reactor/appprofile/GetAppSubgroupFeaturesReactor.java b/src/prerna/reactor/appprofile/GetAppSubgroupFeaturesReactor.java index bdb13d61669..dcb4fc816bc 100644 --- a/src/prerna/reactor/appprofile/GetAppSubgroupFeaturesReactor.java +++ b/src/prerna/reactor/appprofile/GetAppSubgroupFeaturesReactor.java @@ -57,7 +57,7 @@ public NounMetadata execute() { String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); String subgroupId = this.keyValue.get(ReactorKeysEnum.SUBGROUP_ID.getKey()); - if (!AppProfileUtils.canManageProfiles(user, appId)) { + if (!AppProfileUtils.canAssignProfiles(user, appId)) { throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); } List> result = AppProfileUtils.getSubgroupFeatures(appId, subgroupId); diff --git a/src/prerna/reactor/appprofile/GetAppSubgroupsReactor.java b/src/prerna/reactor/appprofile/GetAppSubgroupsReactor.java index 85b008cb166..1d02e16b6ee 100644 --- a/src/prerna/reactor/appprofile/GetAppSubgroupsReactor.java +++ b/src/prerna/reactor/appprofile/GetAppSubgroupsReactor.java @@ -56,7 +56,7 @@ public NounMetadata execute() { String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); - if (!AppProfileUtils.canManageProfiles(user, appId)) { + if (!AppProfileUtils.canAssignProfiles(user, appId)) { throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); } List> result = AppProfileUtils.getSubgroups(appId, profileId); diff --git a/src/prerna/reactor/appprofile/GetUserProfileReactor.java b/src/prerna/reactor/appprofile/GetUserProfileReactor.java index db6a2ff89e0..c9e16ebedcf 100644 --- a/src/prerna/reactor/appprofile/GetUserProfileReactor.java +++ b/src/prerna/reactor/appprofile/GetUserProfileReactor.java @@ -56,7 +56,7 @@ public NounMetadata execute() { String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); String userId = this.keyValue.get(ReactorKeysEnum.USER_ID.getKey()); - if (!AppProfileUtils.canManageProfiles(user, appId)) { + if (!AppProfileUtils.canAssignProfiles(user, appId)) { throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); } List> profiles = AppProfileUtils.getUserProfiles(appId, userId); From 7fbde12a65c3e819d71be98810e63869c4927cc5 Mon Sep 17 00:00:00 2001 From: "Patel, Parth" Date: Thu, 25 Jun 2026 17:53:25 -0400 Subject: [PATCH 6/9] refactor(profiles): move util classes to reactor packages, add Javadoc, convert reads to SQS - Move AppProfileUtils from prerna.auth.utils to prerna.reactor.appprofile - Move PlatformProfileUtils from prerna.auth.utils to prerna.reactor.platformprofile - Add prerna.reactor.appprofile and prerna.reactor.platformprofile to SecurityDb access allowlist in SystemEngineRegistry - Add SecurityProjectUtils import for relocated AppProfileUtils - Convert AppProfileUtils.getFeatures() from PreparedStatement to SelectQueryStruct (simple single-table read); keep correlated subqueries and JOINs as PreparedStatement - Add class-level Javadoc to all 34 appprofile reactors and 10 platform- profile reactors - Add method-level Javadoc to all public static methods in both util classes Co-Authored-By: Claude Sonnet 4.6 --- .../auth/utils/SecurityProjectUtils.java | 1 + .../AddAppProfileManagerReactor.java | 7 +- .../appprofile}/AppProfileUtils.java | 179 ++++++++++++------ .../AssignAppUserProfileReactor.java | 7 +- .../AssignAppUserSubgroupReactor.java | 7 +- .../appprofile/AssignUserProfileReactor.java | 7 +- .../appprofile/CheckAppFeatureReactor.java | 7 +- .../appprofile/CheckFeatureReactor.java | 7 +- .../appprofile/CreateAppFeatureReactor.java | 7 +- .../appprofile/CreateAppProfileReactor.java | 7 +- .../appprofile/CreateAppSubgroupReactor.java | 7 +- .../appprofile/DeleteAppFeatureReactor.java | 7 +- .../appprofile/DeleteAppProfileReactor.java | 7 +- .../appprofile/DeleteAppSubgroupReactor.java | 7 +- .../appprofile/GetAppFeaturesReactor.java | 7 +- .../GetAppProfileFeaturesReactor.java | 7 +- .../GetAppProfileManagersReactor.java | 7 +- .../appprofile/GetAppProfileUsersReactor.java | 7 +- .../appprofile/GetAppProfilesReactor.java | 7 +- .../GetAppSubgroupFeaturesReactor.java | 7 +- .../GetAppSubgroupUsersReactor.java | 7 +- .../appprofile/GetAppSubgroupsReactor.java | 7 +- .../appprofile/GetAppUserFeaturesReactor.java | 7 +- .../appprofile/GetUserAppProfileReactor.java | 7 +- .../appprofile/GetUserAppProfilesReactor.java | 7 +- .../appprofile/GetUserFeaturesReactor.java | 7 +- .../appprofile/GetUserProfileReactor.java | 7 +- .../RemoveAppProfileManagerReactor.java | 7 +- .../RemoveAppUserProfileReactor.java | 7 +- .../RemoveAppUserSubgroupReactor.java | 7 +- .../appprofile/RemoveUserProfileReactor.java | 7 +- .../SetAppProfileFeatureReactor.java | 7 +- .../SetAppSubgroupFeatureReactor.java | 7 +- .../appprofile/UpdateAppFeatureReactor.java | 7 +- .../appprofile/UpdateAppProfileReactor.java | 7 +- .../appprofile/UpdateAppSubgroupReactor.java | 7 +- .../AssignUserPlatformProfileReactor.java | 16 +- .../CreatePlatformProfileReactor.java | 16 +- .../DeletePlatformProfileReactor.java | 14 +- .../GetPlatformFeaturesReactor.java | 14 +- .../GetPlatformProfileUsersReactor.java | 14 +- .../GetPlatformProfilesReactor.java | 9 +- .../GetUserPlatformFeaturesReactor.java | 9 +- .../PlatformProfileUtils.java | 20 +- .../RemoveUserPlatformProfileReactor.java | 14 +- .../SetPlatformFeatureReactor.java | 18 +- .../UpdatePlatformProfileReactor.java | 18 +- src/prerna/util/SystemEngineRegistry.java | 1 + 48 files changed, 445 insertions(+), 136 deletions(-) rename src/prerna/{auth/utils => reactor/appprofile}/AppProfileUtils.java (92%) rename src/prerna/{auth/utils => reactor/platformprofile}/PlatformProfileUtils.java (92%) diff --git a/src/prerna/auth/utils/SecurityProjectUtils.java b/src/prerna/auth/utils/SecurityProjectUtils.java index b47f3d45d9f..8012db58f20 100644 --- a/src/prerna/auth/utils/SecurityProjectUtils.java +++ b/src/prerna/auth/utils/SecurityProjectUtils.java @@ -94,6 +94,7 @@ import prerna.util.Utility; import prerna.util.sql.AbstractSqlQueryUtil; import prerna.util.sql.RdbmsTypeEnum; +import prerna.reactor.appprofile.AppProfileUtils; public class SecurityProjectUtils extends AbstractSecurityUtils { diff --git a/src/prerna/reactor/appprofile/AddAppProfileManagerReactor.java b/src/prerna/reactor/appprofile/AddAppProfileManagerReactor.java index 33f6945a1cc..8d4bafba4af 100644 --- a/src/prerna/reactor/appprofile/AddAppProfileManagerReactor.java +++ b/src/prerna/reactor/appprofile/AddAppProfileManagerReactor.java @@ -33,11 +33,16 @@ import prerna.sablecc2.om.ReactorKeysEnum; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Grant a user delegated 'assign' permission to add/remove users from profiles. + * + *

Pixel: {@code AddAppProfileManager(app=["appId"], userId=["userId"]);}

+ */ public class AddAppProfileManagerReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(AddAppProfileManagerReactor.class); diff --git a/src/prerna/auth/utils/AppProfileUtils.java b/src/prerna/reactor/appprofile/AppProfileUtils.java similarity index 92% rename from src/prerna/auth/utils/AppProfileUtils.java rename to src/prerna/reactor/appprofile/AppProfileUtils.java index 0dab329b469..a0b5171b39e 100644 --- a/src/prerna/auth/utils/AppProfileUtils.java +++ b/src/prerna/reactor/appprofile/AppProfileUtils.java @@ -25,7 +25,7 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.auth.utils; +package prerna.reactor.appprofile; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -42,6 +42,8 @@ import prerna.auth.AccessToken; import prerna.auth.User; +import prerna.auth.utils.SecurityAdminUtils; +import prerna.auth.utils.SecurityProjectUtils; import prerna.engine.api.IRDBMSEngine; import prerna.engine.api.IRawSelectWrapper; import prerna.query.querystruct.SelectQueryStruct; @@ -63,6 +65,10 @@ private AppProfileUtils() { // ─── Permission checks ────────────────────────────────────────────────── + /** + * Returns true if the user has permission to manage profiles for the given app + * (admin, owner, or editor). + */ public static boolean canManageProfiles(User user, String appId) { if (SecurityAdminUtils.userIsAdmin(user)) return true; if (!appExists(appId)) { @@ -99,6 +105,10 @@ public static boolean canAssignProfiles(User user, String appId) { return false; } + /** + * Returns true if the user can evaluate features for the given app (admin, + * profile manager, or any project viewer). + */ public static boolean canEvaluateFeatures(User user, String appId) { if (SecurityAdminUtils.userIsAdmin(user)) return true; if (canAssignProfiles(user, appId)) return true; @@ -120,6 +130,9 @@ private static boolean appExists(String appId) { // ─── Profile CRUD ─────────────────────────────────────────────────────── + /** + * Creates a new named profile for an app and returns its metadata map. + */ public static Map createProfile(String appId, String name, String description, boolean isDefault, boolean isGroup, User user) { if (name == null || name.trim().isEmpty()) { @@ -170,6 +183,10 @@ public static Map createProfile(String appId, String name, Strin return result; } + /** + * Updates mutable fields on an existing app profile (name, description, + * isDefault, isGroup). Null parameters are ignored. + */ public static void updateProfile(String appId, String profileId, String name, String description, Boolean isDefault, Boolean isGroup, User user) { if (name != null) { @@ -215,6 +232,10 @@ public static void updateProfile(String appId, String profileId, String name, St } } + /** + * Deletes a profile and cascades to its feature mappings, subgroups, and + * subgroup assignments. Throws if any users are still assigned. + */ public static void deleteProfile(String appId, String profileId, User user) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); int count = getAssignedUserCount(securityDb, appId, profileId); @@ -230,7 +251,7 @@ public static void deleteProfile(String appId, String profileId, User user) { ps.execute(); if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); } catch (SQLException e) { - classLogger.error("Failed to delete app profile", e); + classLogger.error("Failed to delete app profile {}", profileId, e); throw new IllegalArgumentException("An error occurred deleting the app profile."); } finally { ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); @@ -243,7 +264,7 @@ public static void deleteProfile(String appId, String profileId, User user) { ps.execute(); if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); } catch (SQLException e) { - classLogger.error("Failed to cascade delete app profile features", e); + classLogger.error("Failed to cascade delete app profile features for profile {}", profileId, e); } finally { ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); } @@ -260,12 +281,17 @@ public static void deleteProfile(String appId, String profileId, User user) { ps.execute(); if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); } catch (SQLException e) { - classLogger.error("Failed to cascade delete subgroups for profile", e); + classLogger.error("Failed to cascade delete subgroups for profile {}", profileId, e); } finally { ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); } } + /** + * Returns all profiles for an app, each with a live USER_COUNT from + * APP_USER_PROFILE. Uses a correlated subquery and must remain as + * PreparedStatement. + */ public static List> getProfiles(String appId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); List> profiles = new ArrayList<>(); @@ -300,6 +326,9 @@ public static List> getProfiles(String appId) { // ─── Feature CRUD ─────────────────────────────────────────────────────── + /** + * Creates a new app-level feature definition and returns its metadata map. + */ public static Map createFeature(String appId, String featureKey, String description, User user) { validateFeatureKey(featureKey); @@ -332,6 +361,10 @@ public static Map createFeature(String appId, String featureKey, return result; } + /** + * Updates the key and/or description of an existing app feature. Null + * parameters are ignored. + */ public static void updateFeature(String appId, String featureId, String featureKey, String description, User user) { if (featureKey != null) validateFeatureKey(featureKey); @@ -361,6 +394,10 @@ public static void updateFeature(String appId, String featureId, String featureK } } + /** + * Deletes an app feature and cascades removal from profile and subgroup feature + * mapping tables. + */ public static void deleteFeature(String appId, String featureId, User user) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); PreparedStatement ps = null; @@ -401,36 +438,44 @@ public static void deleteFeature(String appId, String featureId, User user) { } } + /** + * Returns all feature definitions for an app, ordered by FEATURE_KEY ascending. + */ public static List> getFeatures(String appId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); List> features = new ArrayList<>(); - String sql = "SELECT FEATURE_ID, FEATURE_KEY, DESCRIPTION, CREATED_BY, CREATED_AT " - + "FROM APP_FEATURE WHERE APP_ID=? ORDER BY FEATURE_KEY ASC"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(sql); - ps.setString(1, appId); - rs = ps.executeQuery(); - while (rs.next()) { + + SelectQueryStruct sqs = new SelectQueryStruct(); + sqs.addSelector(new QueryColumnSelector("APP_FEATURE__FEATURE_ID")); + sqs.addSelector(new QueryColumnSelector("APP_FEATURE__FEATURE_KEY")); + sqs.addSelector(new QueryColumnSelector("APP_FEATURE__DESCRIPTION")); + sqs.addSelector(new QueryColumnSelector("APP_FEATURE__CREATED_BY")); + sqs.addSelector(new QueryColumnSelector("APP_FEATURE__CREATED_AT")); + sqs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_FEATURE__APP_ID", "==", appId)); + sqs.addOrderBy("APP_FEATURE__FEATURE_KEY", "ASC"); + + try (IRawSelectWrapper wrapper = WrapperManager.getInstance().getRawWrapper(securityDb, sqs)) { + while (wrapper.hasNext()) { + Object[] values = wrapper.next().getValues(); Map feature = new HashMap<>(); - feature.put("featureId", rs.getString("FEATURE_ID")); - feature.put("featureKey", rs.getString("FEATURE_KEY")); - feature.put("description", rs.getString("DESCRIPTION")); - feature.put("createdBy", rs.getString("CREATED_BY")); - feature.put("createdAt", rs.getTimestamp("CREATED_AT")); + feature.put("featureId", values[0]); + feature.put("featureKey", values[1]); + feature.put("description", values[2]); + feature.put("createdBy", values[3]); + feature.put("createdAt", values[4]); features.add(feature); } - } catch (SQLException e) { + } catch (Exception e) { classLogger.error("Failed to get app features", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); } return features; } // ─── Profile-Feature Assignment ───────────────────────────────────────── + /** + * Sets the enabled state of a feature for a profile (upsert via delete+insert). + */ public static void setProfileFeature(String appId, String profileId, String featureId, boolean enabled, User user) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); @@ -466,6 +511,10 @@ public static void setProfileFeature(String appId, String profileId, String feat } } + /** + * Returns all features for an app merged with their enabled state for the given + * profile. Uses two sequential queries joined in code; kept as PreparedStatement. + */ public static List> getProfileFeatures(String appId, String profileId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); List> results = new ArrayList<>(); @@ -675,6 +724,10 @@ public static Map getUserAppProfiles(String appId, User user) { return result; } + /** + * Returns all users assigned to a profile, including display name and email via + * JOIN with SMSS_USER. Kept as PreparedStatement due to the LEFT JOIN. + */ public static List> getProfileUsers(String appId, String profileId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); List> users = new ArrayList<>(); @@ -708,6 +761,10 @@ public static List> getProfileUsers(String appId, String pro // ─── Subgroup CRUD ─────────────────────────────────────────────────────── + /** + * Creates a new named sub-group within a group-style profile and returns its + * metadata map. + */ public static Map createSubgroup(String appId, String profileId, String name, String description, User user) { if (name == null || name.trim().isEmpty()) { @@ -751,6 +808,10 @@ public static Map createSubgroup(String appId, String profileId, return result; } + /** + * Updates the name and/or description of an existing sub-group. Null parameters + * are ignored. + */ public static void updateSubgroup(String appId, String subgroupId, String name, String description, User user) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); @@ -782,6 +843,9 @@ public static void updateSubgroup(String appId, String subgroupId, String name, } } + /** + * Deletes a sub-group and its user and feature assignments. + */ public static void deleteSubgroup(String appId, String subgroupId, User user) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); deleteSubgroupInternal(securityDb, appId, subgroupId); @@ -800,6 +864,10 @@ public static void deleteSubgroup(String appId, String subgroupId, User user) { } } + /** + * Returns all sub-groups for a profile, each with a live USER_COUNT from + * APP_USER_SUBGROUP. Uses a correlated subquery; kept as PreparedStatement. + */ public static List> getSubgroups(String appId, String profileId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); List> result = new ArrayList<>(); @@ -831,6 +899,10 @@ public static List> getSubgroups(String appId, String profil return result; } + /** + * Returns all users assigned to a sub-group, including display name and email + * via JOIN with SMSS_USER. Kept as PreparedStatement due to the LEFT JOIN. + */ public static List> getSubgroupUsers(String appId, String subgroupId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); List> users = new ArrayList<>(); @@ -864,6 +936,10 @@ public static List> getSubgroupUsers(String appId, String su // ─── Subgroup-Feature Assignment ──────────────────────────────────────── + /** + * Sets the enabled state of a feature for a sub-group (upsert via + * delete+insert). + */ public static void setSubgroupFeature(String appId, String subgroupId, String featureId, boolean enabled, User user) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); @@ -899,6 +975,10 @@ public static void setSubgroupFeature(String appId, String subgroupId, String fe } } + /** + * Returns all features for an app merged with their enabled state for the given + * sub-group. Uses two sequential queries joined in code; kept as PreparedStatement. + */ public static List> getSubgroupFeatures(String appId, String subgroupId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); List> results = new ArrayList<>(); @@ -946,6 +1026,9 @@ public static List> getSubgroupFeatures(String appId, String // ─── User-Subgroup Assignment ──────────────────────────────────────────── + /** + * Assigns a user to a sub-group. If already assigned, this is a no-op. + */ public static void assignUserSubgroup(String appId, String userId, String subgroupId, User actor) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); // check for existing assignment @@ -985,6 +1068,9 @@ public static void assignUserSubgroup(String appId, String userId, String subgro } } + /** + * Removes a user from a specific sub-group assignment. + */ public static void removeUserSubgroup(String appId, String userId, String subgroupId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); PreparedStatement ps = null; @@ -1005,6 +1091,10 @@ public static void removeUserSubgroup(String appId, String userId, String subgro // ─── Profile Manager (delegated BU admin) ──────────────────────────────── + /** + * Grants a user delegated 'assign' permission to manage profile assignments for + * an app. If already a manager, this is a no-op. + */ public static void addProfileManager(String appId, String userId, User actor) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); // check for existing @@ -1039,6 +1129,9 @@ public static void addProfileManager(String appId, String userId, User actor) { } } + /** + * Revokes delegated profile manager permission from a user for an app. + */ public static void removeProfileManager(String appId, String userId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); PreparedStatement ps = null; @@ -1055,6 +1148,10 @@ public static void removeProfileManager(String appId, String userId) { } } + /** + * Returns all users with delegated profile manager permission for an app, with + * display name and email via JOIN. Kept as PreparedStatement due to LEFT JOIN. + */ public static List> getProfileManagers(String appId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); List> result = new ArrayList<>(); @@ -1086,6 +1183,11 @@ public static List> getProfileManagers(String appId) { // ─── Feature evaluation ────────────────────────────────────────────────── + /** + * Returns true if the given feature key is enabled for the calling user in the + * app, evaluated across all assigned profiles, subgroups, and the default + * profile fallback. + */ public static boolean checkFeature(String appId, String featureKey, User user) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); String featureId = resolveFeatureId(securityDb, appId, featureKey); @@ -1227,23 +1329,6 @@ private static int getAssignedUserCount(IRDBMSEngine securityDb, String appId, S return 0; } - private static int getSubgroupUserCount(IRDBMSEngine securityDb, String appId, String subgroupId) { - String sql = "SELECT COUNT(*) FROM APP_USER_SUBGROUP WHERE APP_ID=? AND SUBGROUP_ID=?"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(sql); - ps.setString(1, appId); - ps.setString(2, subgroupId); - rs = ps.executeQuery(); - if (rs.next()) return rs.getInt(1); - } catch (SQLException e) { - classLogger.error("Failed to count subgroup users", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); - } - return 0; - } private static Map getDefaultProfile(IRDBMSEngine securityDb, String appId) { String sql = "SELECT PROFILE_ID, PROFILE_NAME, IS_GROUP FROM APP_PROFILE WHERE APP_ID=? AND IS_DEFAULT=TRUE"; @@ -1521,23 +1606,7 @@ private static void deleteSubgroupInternal(IRDBMSEngine securityDb, String appId } } - /** - * Internal-only single-profile lookup, used for backwards compat with old admin reactor. - */ - public static Map getUserProfile(String appId, String userId) { - IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - List> profiles = getExplicitUserProfiles(securityDb, appId, userId); - if (!profiles.isEmpty()) { - Map first = profiles.get(0); - Map result = new HashMap<>(); - result.put("profileId", first.get("profileId")); - result.put("profileName", first.get("profileName")); - result.put("isExplicitAssignment", true); - return result; - } - return getDefaultProfile(securityDb, appId); - } - + /** Returns the primary user ID from the user's active access token. */ public static String getUserId(User user) { if (user == null) return null; AccessToken token = user.getAccessToken(user.getPrimaryLogin()); diff --git a/src/prerna/reactor/appprofile/AssignAppUserProfileReactor.java b/src/prerna/reactor/appprofile/AssignAppUserProfileReactor.java index 9ebb924c6e4..abb60b832a0 100644 --- a/src/prerna/reactor/appprofile/AssignAppUserProfileReactor.java +++ b/src/prerna/reactor/appprofile/AssignAppUserProfileReactor.java @@ -30,13 +30,18 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Assign a user to a profile for an app. A user can be in multiple profiles simultaneously. + * + *

Pixel: {@code AssignAppUserProfile(app=["appId"], userId=["userId"], profile=["profileId"]);}

+ */ public class AssignAppUserProfileReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(AssignAppUserProfileReactor.class); diff --git a/src/prerna/reactor/appprofile/AssignAppUserSubgroupReactor.java b/src/prerna/reactor/appprofile/AssignAppUserSubgroupReactor.java index 6878828b3c1..eed892d00b1 100644 --- a/src/prerna/reactor/appprofile/AssignAppUserSubgroupReactor.java +++ b/src/prerna/reactor/appprofile/AssignAppUserSubgroupReactor.java @@ -30,13 +30,18 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Assign a user to a sub-group. + * + *

Pixel: {@code AssignAppUserSubgroup(app=["appId"], userId=["userId"], subgroup=["subgroupId"]);}

+ */ public class AssignAppUserSubgroupReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(AssignAppUserSubgroupReactor.class); diff --git a/src/prerna/reactor/appprofile/AssignUserProfileReactor.java b/src/prerna/reactor/appprofile/AssignUserProfileReactor.java index 084177c5a05..15facd02d32 100644 --- a/src/prerna/reactor/appprofile/AssignUserProfileReactor.java +++ b/src/prerna/reactor/appprofile/AssignUserProfileReactor.java @@ -30,7 +30,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; @@ -42,6 +42,11 @@ * Kept for backwards compatibility. */ @Deprecated +/** + * Assign a user to a profile for an app. A user can be in multiple profiles simultaneously. + * + *

Pixel: {@code AssignUserProfile(app=["appId"], userId=["userId"], profile=["profileId"]);}

+ */ public class AssignUserProfileReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(AssignUserProfileReactor.class); diff --git a/src/prerna/reactor/appprofile/CheckAppFeatureReactor.java b/src/prerna/reactor/appprofile/CheckAppFeatureReactor.java index 9f7a24b821e..44ecda29ea6 100644 --- a/src/prerna/reactor/appprofile/CheckAppFeatureReactor.java +++ b/src/prerna/reactor/appprofile/CheckAppFeatureReactor.java @@ -30,13 +30,18 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Check whether a feature is enabled for the calling user in an app, across all assigned profiles and subgroups. + * + *

Pixel: {@code CheckAppFeature(app=["appId"], feature=["featureKey"]);}

+ */ public class CheckAppFeatureReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(CheckAppFeatureReactor.class); diff --git a/src/prerna/reactor/appprofile/CheckFeatureReactor.java b/src/prerna/reactor/appprofile/CheckFeatureReactor.java index 2d61b608b71..e767b3921e7 100644 --- a/src/prerna/reactor/appprofile/CheckFeatureReactor.java +++ b/src/prerna/reactor/appprofile/CheckFeatureReactor.java @@ -30,7 +30,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; @@ -41,6 +41,11 @@ * @deprecated Use {@link CheckAppFeatureReactor} (Pixel: CheckAppFeature). */ @Deprecated +/** + * Check whether a feature is enabled for the calling user in an app. + * + *

Pixel: {@code CheckFeature(app=["appId"], feature=["featureKey"]);}

+ */ public class CheckFeatureReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(CheckFeatureReactor.class); diff --git a/src/prerna/reactor/appprofile/CreateAppFeatureReactor.java b/src/prerna/reactor/appprofile/CreateAppFeatureReactor.java index 63c028b2161..9c3c97b1271 100644 --- a/src/prerna/reactor/appprofile/CreateAppFeatureReactor.java +++ b/src/prerna/reactor/appprofile/CreateAppFeatureReactor.java @@ -32,13 +32,18 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Create a feature key for an app. + * + *

Pixel: {@code CreateAppFeature(app=["appId"], feature=["featureKey"], description=["desc"]);}

+ */ public class CreateAppFeatureReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(CreateAppFeatureReactor.class); diff --git a/src/prerna/reactor/appprofile/CreateAppProfileReactor.java b/src/prerna/reactor/appprofile/CreateAppProfileReactor.java index a1790b5ccfe..ed0ff7f0c82 100644 --- a/src/prerna/reactor/appprofile/CreateAppProfileReactor.java +++ b/src/prerna/reactor/appprofile/CreateAppProfileReactor.java @@ -32,13 +32,18 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Create a named profile for an app. + * + *

Pixel: {@code CreateAppProfile(app=["appId"], name=["profileName"]);}

+ */ public class CreateAppProfileReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(CreateAppProfileReactor.class); diff --git a/src/prerna/reactor/appprofile/CreateAppSubgroupReactor.java b/src/prerna/reactor/appprofile/CreateAppSubgroupReactor.java index b5c861c5dfe..5b49795c7e7 100644 --- a/src/prerna/reactor/appprofile/CreateAppSubgroupReactor.java +++ b/src/prerna/reactor/appprofile/CreateAppSubgroupReactor.java @@ -32,13 +32,18 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Create a named sub-group within a group-style profile. + * + *

Pixel: {@code CreateAppSubgroup(app=["appId"], profile=["profileId"], name=["subgroupName"]);}

+ */ public class CreateAppSubgroupReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(CreateAppSubgroupReactor.class); diff --git a/src/prerna/reactor/appprofile/DeleteAppFeatureReactor.java b/src/prerna/reactor/appprofile/DeleteAppFeatureReactor.java index 6688655ee81..c12f0a40eb4 100644 --- a/src/prerna/reactor/appprofile/DeleteAppFeatureReactor.java +++ b/src/prerna/reactor/appprofile/DeleteAppFeatureReactor.java @@ -30,13 +30,18 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Delete a feature key from an app. + * + *

Pixel: {@code DeleteAppFeature(app=["appId"], feature=["featureId"]);}

+ */ public class DeleteAppFeatureReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(DeleteAppFeatureReactor.class); diff --git a/src/prerna/reactor/appprofile/DeleteAppProfileReactor.java b/src/prerna/reactor/appprofile/DeleteAppProfileReactor.java index 35a522b54ce..17a31431b76 100644 --- a/src/prerna/reactor/appprofile/DeleteAppProfileReactor.java +++ b/src/prerna/reactor/appprofile/DeleteAppProfileReactor.java @@ -30,13 +30,18 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Delete a named profile from an app. + * + *

Pixel: {@code DeleteAppProfile(app=["appId"], profile=["profileId"]);}

+ */ public class DeleteAppProfileReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(DeleteAppProfileReactor.class); diff --git a/src/prerna/reactor/appprofile/DeleteAppSubgroupReactor.java b/src/prerna/reactor/appprofile/DeleteAppSubgroupReactor.java index 0898afeb3b7..e2981d064f2 100644 --- a/src/prerna/reactor/appprofile/DeleteAppSubgroupReactor.java +++ b/src/prerna/reactor/appprofile/DeleteAppSubgroupReactor.java @@ -30,13 +30,18 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Delete a sub-group and its user/feature assignments. + * + *

Pixel: {@code DeleteAppSubgroup(app=["appId"], subgroup=["subgroupId"]);}

+ */ public class DeleteAppSubgroupReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(DeleteAppSubgroupReactor.class); diff --git a/src/prerna/reactor/appprofile/GetAppFeaturesReactor.java b/src/prerna/reactor/appprofile/GetAppFeaturesReactor.java index b425ea1a0a5..48bee4c1c6a 100644 --- a/src/prerna/reactor/appprofile/GetAppFeaturesReactor.java +++ b/src/prerna/reactor/appprofile/GetAppFeaturesReactor.java @@ -33,13 +33,18 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Get all feature keys defined for an app. + * + *

Pixel: {@code GetAppFeatures(app=["appId"]);}

+ */ public class GetAppFeaturesReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(GetAppFeaturesReactor.class); diff --git a/src/prerna/reactor/appprofile/GetAppProfileFeaturesReactor.java b/src/prerna/reactor/appprofile/GetAppProfileFeaturesReactor.java index a6726b7373c..095f7c76bae 100644 --- a/src/prerna/reactor/appprofile/GetAppProfileFeaturesReactor.java +++ b/src/prerna/reactor/appprofile/GetAppProfileFeaturesReactor.java @@ -36,11 +36,16 @@ import prerna.sablecc2.om.ReactorKeysEnum; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Get all features with their enabled status for a profile. + * + *

Pixel: {@code GetAppProfileFeatures(app=["appId"], profile=["profileId"]);}

+ */ public class GetAppProfileFeaturesReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(GetAppProfileFeaturesReactor.class); diff --git a/src/prerna/reactor/appprofile/GetAppProfileManagersReactor.java b/src/prerna/reactor/appprofile/GetAppProfileManagersReactor.java index cdae7a670b9..2cfb289281a 100644 --- a/src/prerna/reactor/appprofile/GetAppProfileManagersReactor.java +++ b/src/prerna/reactor/appprofile/GetAppProfileManagersReactor.java @@ -36,11 +36,16 @@ import prerna.sablecc2.om.ReactorKeysEnum; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Get all users with delegated profile manager permission for an app. + * + *

Pixel: {@code GetAppProfileManagers(app=["appId"]);}

+ */ public class GetAppProfileManagersReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(GetAppProfileManagersReactor.class); diff --git a/src/prerna/reactor/appprofile/GetAppProfileUsersReactor.java b/src/prerna/reactor/appprofile/GetAppProfileUsersReactor.java index dde557a3046..005545fa722 100644 --- a/src/prerna/reactor/appprofile/GetAppProfileUsersReactor.java +++ b/src/prerna/reactor/appprofile/GetAppProfileUsersReactor.java @@ -36,11 +36,16 @@ import prerna.sablecc2.om.ReactorKeysEnum; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Get all users assigned to a profile in an app. + * + *

Pixel: {@code GetAppProfileUsers(app=["appId"], profile=["profileId"]);}

+ */ public class GetAppProfileUsersReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(GetAppProfileUsersReactor.class); diff --git a/src/prerna/reactor/appprofile/GetAppProfilesReactor.java b/src/prerna/reactor/appprofile/GetAppProfilesReactor.java index af04372faa2..779c469fcd1 100644 --- a/src/prerna/reactor/appprofile/GetAppProfilesReactor.java +++ b/src/prerna/reactor/appprofile/GetAppProfilesReactor.java @@ -33,13 +33,18 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Get all profiles defined for an app. + * + *

Pixel: {@code GetAppProfiles(app=["appId"]);}

+ */ public class GetAppProfilesReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(GetAppProfilesReactor.class); diff --git a/src/prerna/reactor/appprofile/GetAppSubgroupFeaturesReactor.java b/src/prerna/reactor/appprofile/GetAppSubgroupFeaturesReactor.java index dcb4fc816bc..2aa24975ffc 100644 --- a/src/prerna/reactor/appprofile/GetAppSubgroupFeaturesReactor.java +++ b/src/prerna/reactor/appprofile/GetAppSubgroupFeaturesReactor.java @@ -36,11 +36,16 @@ import prerna.sablecc2.om.ReactorKeysEnum; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Get all feature flags for a sub-group. + * + *

Pixel: {@code GetAppSubgroupFeatures(app=["appId"], subgroup=["subgroupId"]);}

+ */ public class GetAppSubgroupFeaturesReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(GetAppSubgroupFeaturesReactor.class); diff --git a/src/prerna/reactor/appprofile/GetAppSubgroupUsersReactor.java b/src/prerna/reactor/appprofile/GetAppSubgroupUsersReactor.java index 54450e45d33..58d7376d6fa 100644 --- a/src/prerna/reactor/appprofile/GetAppSubgroupUsersReactor.java +++ b/src/prerna/reactor/appprofile/GetAppSubgroupUsersReactor.java @@ -36,11 +36,16 @@ import prerna.sablecc2.om.ReactorKeysEnum; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Get all users assigned to a sub-group. + * + *

Pixel: {@code GetAppSubgroupUsers(app=["appId"], subgroup=["subgroupId"]);}

+ */ public class GetAppSubgroupUsersReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(GetAppSubgroupUsersReactor.class); diff --git a/src/prerna/reactor/appprofile/GetAppSubgroupsReactor.java b/src/prerna/reactor/appprofile/GetAppSubgroupsReactor.java index 1d02e16b6ee..66205cc9efc 100644 --- a/src/prerna/reactor/appprofile/GetAppSubgroupsReactor.java +++ b/src/prerna/reactor/appprofile/GetAppSubgroupsReactor.java @@ -33,13 +33,18 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Get all sub-groups within a group-style profile. + * + *

Pixel: {@code GetAppSubgroups(app=["appId"], profile=["profileId"]);}

+ */ public class GetAppSubgroupsReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(GetAppSubgroupsReactor.class); diff --git a/src/prerna/reactor/appprofile/GetAppUserFeaturesReactor.java b/src/prerna/reactor/appprofile/GetAppUserFeaturesReactor.java index 0b1e2b70b69..e51ce0d64a7 100644 --- a/src/prerna/reactor/appprofile/GetAppUserFeaturesReactor.java +++ b/src/prerna/reactor/appprofile/GetAppUserFeaturesReactor.java @@ -32,13 +32,18 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Get all enabled features for the calling user in an app, unioned across all assigned profiles and subgroups. + * + *

Pixel: {@code GetAppUserFeatures(app=["appId"]);}

+ */ public class GetAppUserFeaturesReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(GetAppUserFeaturesReactor.class); diff --git a/src/prerna/reactor/appprofile/GetUserAppProfileReactor.java b/src/prerna/reactor/appprofile/GetUserAppProfileReactor.java index 84a1951f2aa..c2f254f2422 100644 --- a/src/prerna/reactor/appprofile/GetUserAppProfileReactor.java +++ b/src/prerna/reactor/appprofile/GetUserAppProfileReactor.java @@ -32,7 +32,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; @@ -43,6 +43,11 @@ * @deprecated Use {@link GetUserAppProfilesReactor} (Pixel: GetUserAppProfiles). Identical behavior. */ @Deprecated +/** + * Alias for GetUserAppProfiles — identical behavior. Get the calling user's profile and subgroup memberships for an app. + * + *

Pixel: {@code GetUserAppProfile(app=["appId"]);}

+ */ public class GetUserAppProfileReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(GetUserAppProfileReactor.class); diff --git a/src/prerna/reactor/appprofile/GetUserAppProfilesReactor.java b/src/prerna/reactor/appprofile/GetUserAppProfilesReactor.java index c50dbd21096..fcb2dab9b25 100644 --- a/src/prerna/reactor/appprofile/GetUserAppProfilesReactor.java +++ b/src/prerna/reactor/appprofile/GetUserAppProfilesReactor.java @@ -32,13 +32,18 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Get all profile and subgroup memberships for the calling user in an app. + * + *

Pixel: {@code GetUserAppProfiles(app=["appId"]);}

+ */ public class GetUserAppProfilesReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(GetUserAppProfilesReactor.class); diff --git a/src/prerna/reactor/appprofile/GetUserFeaturesReactor.java b/src/prerna/reactor/appprofile/GetUserFeaturesReactor.java index 05b174f64a7..a74c488bade 100644 --- a/src/prerna/reactor/appprofile/GetUserFeaturesReactor.java +++ b/src/prerna/reactor/appprofile/GetUserFeaturesReactor.java @@ -32,7 +32,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; @@ -43,6 +43,11 @@ * @deprecated Use {@link GetAppUserFeaturesReactor} (Pixel: GetAppUserFeatures). */ @Deprecated +/** + * Get all enabled features for the calling user in an app. + * + *

Pixel: {@code GetUserFeatures(app=["appId"]);}

+ */ public class GetUserFeaturesReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(GetUserFeaturesReactor.class); diff --git a/src/prerna/reactor/appprofile/GetUserProfileReactor.java b/src/prerna/reactor/appprofile/GetUserProfileReactor.java index c9e16ebedcf..57e32fe881a 100644 --- a/src/prerna/reactor/appprofile/GetUserProfileReactor.java +++ b/src/prerna/reactor/appprofile/GetUserProfileReactor.java @@ -33,13 +33,18 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Get all profile assignments for a specific user in an app. Requires manage permission. + * + *

Pixel: {@code GetUserProfile(app=["appId"], userId=["userId"]);}

+ */ public class GetUserProfileReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(GetUserProfileReactor.class); diff --git a/src/prerna/reactor/appprofile/RemoveAppProfileManagerReactor.java b/src/prerna/reactor/appprofile/RemoveAppProfileManagerReactor.java index e040c95a6b5..56e726a751f 100644 --- a/src/prerna/reactor/appprofile/RemoveAppProfileManagerReactor.java +++ b/src/prerna/reactor/appprofile/RemoveAppProfileManagerReactor.java @@ -33,11 +33,16 @@ import prerna.sablecc2.om.ReactorKeysEnum; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Revoke a user's delegated profile manager permission. + * + *

Pixel: {@code RemoveAppProfileManager(app=["appId"], userId=["userId"]);}

+ */ public class RemoveAppProfileManagerReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(RemoveAppProfileManagerReactor.class); diff --git a/src/prerna/reactor/appprofile/RemoveAppUserProfileReactor.java b/src/prerna/reactor/appprofile/RemoveAppUserProfileReactor.java index a2371acefb1..895ba9e6019 100644 --- a/src/prerna/reactor/appprofile/RemoveAppUserProfileReactor.java +++ b/src/prerna/reactor/appprofile/RemoveAppUserProfileReactor.java @@ -30,13 +30,18 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Remove a user from a specific profile assignment for an app. + * + *

Pixel: {@code RemoveAppUserProfile(app=["appId"], userId=["userId"], profile=["profileId"]);}

+ */ public class RemoveAppUserProfileReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(RemoveAppUserProfileReactor.class); diff --git a/src/prerna/reactor/appprofile/RemoveAppUserSubgroupReactor.java b/src/prerna/reactor/appprofile/RemoveAppUserSubgroupReactor.java index d2ab0053786..33f4f4a4495 100644 --- a/src/prerna/reactor/appprofile/RemoveAppUserSubgroupReactor.java +++ b/src/prerna/reactor/appprofile/RemoveAppUserSubgroupReactor.java @@ -30,13 +30,18 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Remove a user from a sub-group. + * + *

Pixel: {@code RemoveAppUserSubgroup(app=["appId"], userId=["userId"], subgroup=["subgroupId"]);}

+ */ public class RemoveAppUserSubgroupReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(RemoveAppUserSubgroupReactor.class); diff --git a/src/prerna/reactor/appprofile/RemoveUserProfileReactor.java b/src/prerna/reactor/appprofile/RemoveUserProfileReactor.java index 9ed991f0422..3b967d76095 100644 --- a/src/prerna/reactor/appprofile/RemoveUserProfileReactor.java +++ b/src/prerna/reactor/appprofile/RemoveUserProfileReactor.java @@ -30,7 +30,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; @@ -44,6 +44,11 @@ * Kept for backwards compatibility. */ @Deprecated +/** + * Remove a user from all profile assignments for an app. + * + *

Pixel: {@code RemoveUserProfile(app=["appId"], userId=["userId"]);}

+ */ public class RemoveUserProfileReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(RemoveUserProfileReactor.class); diff --git a/src/prerna/reactor/appprofile/SetAppProfileFeatureReactor.java b/src/prerna/reactor/appprofile/SetAppProfileFeatureReactor.java index dbf5236dcc7..60c619db784 100644 --- a/src/prerna/reactor/appprofile/SetAppProfileFeatureReactor.java +++ b/src/prerna/reactor/appprofile/SetAppProfileFeatureReactor.java @@ -33,11 +33,16 @@ import prerna.sablecc2.om.ReactorKeysEnum; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Enable or disable a feature for a profile. + * + *

Pixel: {@code SetAppProfileFeature(app=["appId"], profile=["profileId"], feature=["featureId"], enabled=["true"]);}

+ */ public class SetAppProfileFeatureReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(SetAppProfileFeatureReactor.class); diff --git a/src/prerna/reactor/appprofile/SetAppSubgroupFeatureReactor.java b/src/prerna/reactor/appprofile/SetAppSubgroupFeatureReactor.java index 9584a4287a6..d2a87c024ef 100644 --- a/src/prerna/reactor/appprofile/SetAppSubgroupFeatureReactor.java +++ b/src/prerna/reactor/appprofile/SetAppSubgroupFeatureReactor.java @@ -33,11 +33,16 @@ import prerna.sablecc2.om.ReactorKeysEnum; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Enable or disable a feature for a sub-group. + * + *

Pixel: {@code SetAppSubgroupFeature(app=["appId"], subgroup=["subgroupId"], feature=["featureId"], enabled=["true"]);}

+ */ public class SetAppSubgroupFeatureReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(SetAppSubgroupFeatureReactor.class); diff --git a/src/prerna/reactor/appprofile/UpdateAppFeatureReactor.java b/src/prerna/reactor/appprofile/UpdateAppFeatureReactor.java index 9acc501334f..e43f544c47a 100644 --- a/src/prerna/reactor/appprofile/UpdateAppFeatureReactor.java +++ b/src/prerna/reactor/appprofile/UpdateAppFeatureReactor.java @@ -30,13 +30,18 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Update a feature key for an app. + * + *

Pixel: {@code UpdateAppFeature(app=["appId"], feature=["featureId"], featureKey=["newKey"]);}

+ */ public class UpdateAppFeatureReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(UpdateAppFeatureReactor.class); diff --git a/src/prerna/reactor/appprofile/UpdateAppProfileReactor.java b/src/prerna/reactor/appprofile/UpdateAppProfileReactor.java index 2fb2d2e2715..84bd762271c 100644 --- a/src/prerna/reactor/appprofile/UpdateAppProfileReactor.java +++ b/src/prerna/reactor/appprofile/UpdateAppProfileReactor.java @@ -30,13 +30,18 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Update a named profile for an app. + * + *

Pixel: {@code UpdateAppProfile(app=["appId"], profile=["profileId"], name=["newName"]);}

+ */ public class UpdateAppProfileReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(UpdateAppProfileReactor.class); diff --git a/src/prerna/reactor/appprofile/UpdateAppSubgroupReactor.java b/src/prerna/reactor/appprofile/UpdateAppSubgroupReactor.java index 7b58d9e8909..a5deeec9c22 100644 --- a/src/prerna/reactor/appprofile/UpdateAppSubgroupReactor.java +++ b/src/prerna/reactor/appprofile/UpdateAppSubgroupReactor.java @@ -30,13 +30,18 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.auth.utils.AppProfileUtils; +import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Update a sub-group's name or description. + * + *

Pixel: {@code UpdateAppSubgroup(app=["appId"], subgroup=["subgroupId"], name=["newName"]);}

+ */ public class UpdateAppSubgroupReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(UpdateAppSubgroupReactor.class); diff --git a/src/prerna/reactor/platformprofile/AssignUserPlatformProfileReactor.java b/src/prerna/reactor/platformprofile/AssignUserPlatformProfileReactor.java index ac29abf4185..881b67e59e7 100644 --- a/src/prerna/reactor/platformprofile/AssignUserPlatformProfileReactor.java +++ b/src/prerna/reactor/platformprofile/AssignUserPlatformProfileReactor.java @@ -28,15 +28,21 @@ package prerna.reactor.platformprofile; import prerna.auth.User; -import prerna.auth.utils.PlatformProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Assigns a user to a platform profile, replacing any existing profile assignment for that user. + * + *

Pixel: {@code AssignUserPlatformProfile(userId=[""], profileId=[""]);}

+ */ public class AssignUserPlatformProfileReactor extends AbstractReactor { public AssignUserPlatformProfileReactor() { - this.keysToGet = new String[] { "userId", "profileId" }; + this.keysToGet = new String[] { ReactorKeysEnum.USER_ID.getKey(), ReactorKeysEnum.PROFILE_ID.getKey() }; this.keyRequired = new int[] { 1, 1 }; } @@ -47,10 +53,10 @@ public NounMetadata execute() { if (!PlatformProfileUtils.canManage(user)) { throw new IllegalArgumentException("User must be an admin to manage platform profiles."); } - String userId = this.keyValue.get("userId"); - String profileId = this.keyValue.get("profileId"); + String userId = this.keyValue.get(ReactorKeysEnum.USER_ID.getKey()); + String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); PlatformProfileUtils.assignUserProfile(userId, profileId, user); - NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("User assigned to platform profile.")); return noun; } diff --git a/src/prerna/reactor/platformprofile/CreatePlatformProfileReactor.java b/src/prerna/reactor/platformprofile/CreatePlatformProfileReactor.java index 49e46ff524d..9eea95476c9 100644 --- a/src/prerna/reactor/platformprofile/CreatePlatformProfileReactor.java +++ b/src/prerna/reactor/platformprofile/CreatePlatformProfileReactor.java @@ -30,15 +30,21 @@ import java.util.Map; import prerna.auth.User; -import prerna.auth.utils.PlatformProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Creates a new platform profile with the given name and optional description. + * + *

Pixel: {@code CreatePlatformProfile(name=["My Profile"], description=["Optional description"]);}

+ */ public class CreatePlatformProfileReactor extends AbstractReactor { public CreatePlatformProfileReactor() { - this.keysToGet = new String[] { "name", "description" }; + this.keysToGet = new String[] { ReactorKeysEnum.NAME.getKey(), ReactorKeysEnum.DESCRIPTION.getKey() }; this.keyRequired = new int[] { 1, 0 }; } @@ -49,10 +55,10 @@ public NounMetadata execute() { if (!PlatformProfileUtils.canManage(user)) { throw new IllegalArgumentException("User must be an admin to manage platform profiles."); } - String name = this.keyValue.get("name"); - String description = this.keyValue.get("description"); + String name = this.keyValue.get(ReactorKeysEnum.NAME.getKey()); + String description = this.keyValue.get(ReactorKeysEnum.DESCRIPTION.getKey()); Map result = PlatformProfileUtils.createProfile(name, description, user); - NounMetadata noun = new NounMetadata(result, PixelDataType.MAP); + NounMetadata noun = new NounMetadata(result, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Platform profile created.")); return noun; } diff --git a/src/prerna/reactor/platformprofile/DeletePlatformProfileReactor.java b/src/prerna/reactor/platformprofile/DeletePlatformProfileReactor.java index 9c4563a0981..0b041569934 100644 --- a/src/prerna/reactor/platformprofile/DeletePlatformProfileReactor.java +++ b/src/prerna/reactor/platformprofile/DeletePlatformProfileReactor.java @@ -28,15 +28,21 @@ package prerna.reactor.platformprofile; import prerna.auth.User; -import prerna.auth.utils.PlatformProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Deletes a platform profile; fails if any users are still assigned to it. + * + *

Pixel: {@code DeletePlatformProfile(profileId=[""]);}

+ */ public class DeletePlatformProfileReactor extends AbstractReactor { public DeletePlatformProfileReactor() { - this.keysToGet = new String[] { "profileId" }; + this.keysToGet = new String[] { ReactorKeysEnum.PROFILE_ID.getKey() }; this.keyRequired = new int[] { 1 }; } @@ -47,9 +53,9 @@ public NounMetadata execute() { if (!PlatformProfileUtils.canManage(user)) { throw new IllegalArgumentException("User must be an admin to manage platform profiles."); } - String profileId = this.keyValue.get("profileId"); + String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); PlatformProfileUtils.deleteProfile(profileId, user); - NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Platform profile deleted.")); return noun; } diff --git a/src/prerna/reactor/platformprofile/GetPlatformFeaturesReactor.java b/src/prerna/reactor/platformprofile/GetPlatformFeaturesReactor.java index df574374a21..449f5851f5f 100644 --- a/src/prerna/reactor/platformprofile/GetPlatformFeaturesReactor.java +++ b/src/prerna/reactor/platformprofile/GetPlatformFeaturesReactor.java @@ -30,15 +30,21 @@ import java.util.Map; import prerna.auth.User; -import prerna.auth.utils.PlatformProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Gets all predefined platform nav features with their enabled status for a given profile. + * + *

Pixel: {@code GetPlatformFeatures(profileId=[""]);}

+ */ public class GetPlatformFeaturesReactor extends AbstractReactor { public GetPlatformFeaturesReactor() { - this.keysToGet = new String[] { "profileId" }; + this.keysToGet = new String[] { ReactorKeysEnum.PROFILE_ID.getKey() }; this.keyRequired = new int[] { 1 }; } @@ -49,9 +55,9 @@ public NounMetadata execute() { if (!PlatformProfileUtils.canManage(user)) { throw new IllegalArgumentException("User must be an admin to manage platform profiles."); } - String profileId = this.keyValue.get("profileId"); + String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); Map features = PlatformProfileUtils.getProfileFeatures(profileId); - return new NounMetadata(features, PixelDataType.MAP); + return new NounMetadata(features, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); } @Override diff --git a/src/prerna/reactor/platformprofile/GetPlatformProfileUsersReactor.java b/src/prerna/reactor/platformprofile/GetPlatformProfileUsersReactor.java index 01aab9eb4d8..19dd332472e 100644 --- a/src/prerna/reactor/platformprofile/GetPlatformProfileUsersReactor.java +++ b/src/prerna/reactor/platformprofile/GetPlatformProfileUsersReactor.java @@ -31,15 +31,21 @@ import java.util.Map; import prerna.auth.User; -import prerna.auth.utils.PlatformProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Gets all users assigned to a given platform profile, including their name, email, and assignment metadata. + * + *

Pixel: {@code GetPlatformProfileUsers(profileId=[""]);}

+ */ public class GetPlatformProfileUsersReactor extends AbstractReactor { public GetPlatformProfileUsersReactor() { - this.keysToGet = new String[] { "profileId" }; + this.keysToGet = new String[] { ReactorKeysEnum.PROFILE_ID.getKey() }; this.keyRequired = new int[] { 1 }; } @@ -50,9 +56,9 @@ public NounMetadata execute() { if (!PlatformProfileUtils.canManage(user)) { throw new IllegalArgumentException("User must be an admin to manage platform profiles."); } - String profileId = this.keyValue.get("profileId"); + String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); List> users = PlatformProfileUtils.getPlatformProfileUsers(profileId); - return new NounMetadata(users, PixelDataType.CUSTOM_DATA_STRUCTURE); + return new NounMetadata(users, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); } @Override diff --git a/src/prerna/reactor/platformprofile/GetPlatformProfilesReactor.java b/src/prerna/reactor/platformprofile/GetPlatformProfilesReactor.java index 7a75ed68948..65d2a26595b 100644 --- a/src/prerna/reactor/platformprofile/GetPlatformProfilesReactor.java +++ b/src/prerna/reactor/platformprofile/GetPlatformProfilesReactor.java @@ -31,11 +31,16 @@ import java.util.Map; import prerna.auth.User; -import prerna.auth.utils.PlatformProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Gets all platform profiles, each with its id, name, description, creator, creation time, and assigned user count. + * + *

Pixel: {@code GetPlatformProfiles();}

+ */ public class GetPlatformProfilesReactor extends AbstractReactor { public GetPlatformProfilesReactor() { @@ -51,7 +56,7 @@ public NounMetadata execute() { throw new IllegalArgumentException("User must be an admin to manage platform profiles."); } List> profiles = PlatformProfileUtils.getProfiles(user); - return new NounMetadata(profiles, PixelDataType.CUSTOM_DATA_STRUCTURE); + return new NounMetadata(profiles, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); } @Override diff --git a/src/prerna/reactor/platformprofile/GetUserPlatformFeaturesReactor.java b/src/prerna/reactor/platformprofile/GetUserPlatformFeaturesReactor.java index 94380762fbe..f2554605668 100644 --- a/src/prerna/reactor/platformprofile/GetUserPlatformFeaturesReactor.java +++ b/src/prerna/reactor/platformprofile/GetUserPlatformFeaturesReactor.java @@ -30,11 +30,16 @@ import java.util.Map; import prerna.auth.User; -import prerna.auth.utils.PlatformProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Gets platform nav feature visibility for the calling user; users with no assigned profile receive all features enabled (fail-open). + * + *

Pixel: {@code GetUserPlatformFeatures();}

+ */ public class GetUserPlatformFeaturesReactor extends AbstractReactor { public GetUserPlatformFeaturesReactor() { @@ -48,7 +53,7 @@ public NounMetadata execute() { User user = this.insight.getUser(); // Returns all predefined keys → true for unassigned users (fail-open at platform level) Map features = PlatformProfileUtils.getUserFeatures(user); - return new NounMetadata(features, PixelDataType.MAP); + return new NounMetadata(features, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); } @Override diff --git a/src/prerna/auth/utils/PlatformProfileUtils.java b/src/prerna/reactor/platformprofile/PlatformProfileUtils.java similarity index 92% rename from src/prerna/auth/utils/PlatformProfileUtils.java rename to src/prerna/reactor/platformprofile/PlatformProfileUtils.java index 00745e8d258..00cfca043e8 100644 --- a/src/prerna/auth/utils/PlatformProfileUtils.java +++ b/src/prerna/reactor/platformprofile/PlatformProfileUtils.java @@ -25,7 +25,7 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.auth.utils; +package prerna.reactor.platformprofile; import java.sql.PreparedStatement; import java.sql.SQLException; @@ -73,18 +73,20 @@ private PlatformProfileUtils() { // ─── Permission check ──────────────────────────────────────────────────── + /** Returns {@code true} if the given user has admin privileges to manage platform profiles. */ public static boolean canManage(User user) { - return SecurityAdminUtils.userIsAdmin(user); + return prerna.auth.utils.SecurityAdminUtils.userIsAdmin(user); } // ─── Profile CRUD ──────────────────────────────────────────────────────── + /** Creates a new platform profile with the given name and description and returns its id, name, and description. */ public static Map createProfile(String name, String description, User user) { if (name == null || name.trim().isEmpty()) { throw new IllegalArgumentException("Profile name cannot be blank."); } String profileId = UUID.randomUUID().toString(); - String actorId = AppProfileUtils.getUserId(user); + String actorId = prerna.reactor.appprofile.AppProfileUtils.getUserId(user); IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); PreparedStatement ps = null; try { @@ -111,6 +113,7 @@ public static Map createProfile(String name, String description, return result; } + /** Updates the name and/or description of an existing platform profile identified by {@code profileId}. */ public static void updateProfile(String profileId, String name, String description, User user) { if (name != null && name.trim().isEmpty()) { throw new IllegalArgumentException("Profile name cannot be blank."); @@ -140,6 +143,7 @@ public static void updateProfile(String profileId, String name, String descripti } } + /** Deletes a platform profile and its feature rows; throws if any users are still assigned to it. */ public static void deleteProfile(String profileId, User user) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); int count = getAssignedUserCount(securityDb, profileId); @@ -172,6 +176,7 @@ public static void deleteProfile(String profileId, User user) { } } + /** Returns all platform profiles ordered by name, each with id, name, description, createdBy, createdAt, and userCount. */ public static List> getProfiles(User user) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); List> profiles = new ArrayList<>(); @@ -205,6 +210,7 @@ public static List> getProfiles(User user) { // ─── Profile-Feature Assignment ────────────────────────────────────────── + /** Sets the enabled state of a predefined platform nav feature for the specified profile; replaces any existing row. */ public static void setProfileFeature(String profileId, String featureKey, boolean enabled, User user) { if (!PREDEFINED_FEATURE_KEYS.contains(featureKey)) { throw new IllegalArgumentException( @@ -242,6 +248,7 @@ public static void setProfileFeature(String profileId, String featureKey, boolea } } + /** Returns all predefined platform feature keys with their enabled status for the given profile; unknown keys default to {@code false}. */ public static Map getProfileFeatures(String profileId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); Map result = new LinkedHashMap<>(); @@ -273,9 +280,10 @@ public static Map getProfileFeatures(String profileId) { // ─── User-Profile Assignment ───────────────────────────────────────────── + /** Assigns a user to a platform profile, replacing any existing assignment for that user. */ public static void assignUserProfile(String userId, String profileId, User actor) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - String actorId = AppProfileUtils.getUserId(actor); + String actorId = prerna.reactor.appprofile.AppProfileUtils.getUserId(actor); PreparedStatement ps = null; try { ps = securityDb.getPreparedStatement("DELETE FROM PLATFORM_USER_PROFILE WHERE USER_ID=?"); @@ -305,6 +313,7 @@ public static void assignUserProfile(String userId, String profileId, User actor } } + /** Removes the platform profile assignment for the specified user. */ public static void removeUserProfile(String userId, User actor) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); PreparedStatement ps = null; @@ -329,7 +338,7 @@ public static void removeUserProfile(String userId, User actor) { */ public static Map getUserFeatures(User user) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - String userId = AppProfileUtils.getUserId(user); + String userId = prerna.reactor.appprofile.AppProfileUtils.getUserId(user); String profileId = getAssignedProfileId(securityDb, userId); if (profileId == null) { Map all = new LinkedHashMap<>(); @@ -341,6 +350,7 @@ public static Map getUserFeatures(User user) { return getProfileFeatures(profileId); } + /** Returns the list of users assigned to the given platform profile with their name, email, and assignment metadata. */ public static List> getPlatformProfileUsers(String profileId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); List> users = new ArrayList<>(); diff --git a/src/prerna/reactor/platformprofile/RemoveUserPlatformProfileReactor.java b/src/prerna/reactor/platformprofile/RemoveUserPlatformProfileReactor.java index 42fddabe6ce..e7cfd3f4fb1 100644 --- a/src/prerna/reactor/platformprofile/RemoveUserPlatformProfileReactor.java +++ b/src/prerna/reactor/platformprofile/RemoveUserPlatformProfileReactor.java @@ -28,15 +28,21 @@ package prerna.reactor.platformprofile; import prerna.auth.User; -import prerna.auth.utils.PlatformProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Removes a user's platform profile assignment, reverting them to the default fail-open feature set. + * + *

Pixel: {@code RemoveUserPlatformProfile(userId=[""]);}

+ */ public class RemoveUserPlatformProfileReactor extends AbstractReactor { public RemoveUserPlatformProfileReactor() { - this.keysToGet = new String[] { "userId" }; + this.keysToGet = new String[] { ReactorKeysEnum.USER_ID.getKey() }; this.keyRequired = new int[] { 1 }; } @@ -47,9 +53,9 @@ public NounMetadata execute() { if (!PlatformProfileUtils.canManage(user)) { throw new IllegalArgumentException("User must be an admin to manage platform profiles."); } - String userId = this.keyValue.get("userId"); + String userId = this.keyValue.get(ReactorKeysEnum.USER_ID.getKey()); PlatformProfileUtils.removeUserProfile(userId, user); - NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("User removed from platform profile.")); return noun; } diff --git a/src/prerna/reactor/platformprofile/SetPlatformFeatureReactor.java b/src/prerna/reactor/platformprofile/SetPlatformFeatureReactor.java index d77acfc5e55..b704f648de9 100644 --- a/src/prerna/reactor/platformprofile/SetPlatformFeatureReactor.java +++ b/src/prerna/reactor/platformprofile/SetPlatformFeatureReactor.java @@ -28,15 +28,21 @@ package prerna.reactor.platformprofile; import prerna.auth.User; -import prerna.auth.utils.PlatformProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Enables or disables a predefined platform nav feature for a given profile. + * + *

Pixel: {@code SetPlatformFeature(profileId=[""], featureKey=["nav.app-catalog"], enabled=["true"]);}

+ */ public class SetPlatformFeatureReactor extends AbstractReactor { public SetPlatformFeatureReactor() { - this.keysToGet = new String[] { "profileId", "featureKey", "enabled" }; + this.keysToGet = new String[] { ReactorKeysEnum.PROFILE_ID.getKey(), ReactorKeysEnum.FEATURE_KEY.getKey(), ReactorKeysEnum.ENABLED.getKey() }; this.keyRequired = new int[] { 1, 1, 1 }; } @@ -47,11 +53,11 @@ public NounMetadata execute() { if (!PlatformProfileUtils.canManage(user)) { throw new IllegalArgumentException("User must be an admin to manage platform profiles."); } - String profileId = this.keyValue.get("profileId"); - String featureKey = this.keyValue.get("featureKey"); - boolean enabled = Boolean.parseBoolean(this.keyValue.get("enabled")); + String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); + String featureKey = this.keyValue.get(ReactorKeysEnum.FEATURE_KEY.getKey()); + boolean enabled = Boolean.parseBoolean(this.keyValue.get(ReactorKeysEnum.ENABLED.getKey())); PlatformProfileUtils.setProfileFeature(profileId, featureKey, enabled, user); - NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Platform feature updated.")); return noun; } diff --git a/src/prerna/reactor/platformprofile/UpdatePlatformProfileReactor.java b/src/prerna/reactor/platformprofile/UpdatePlatformProfileReactor.java index f6247cb138d..6ee70d0235d 100644 --- a/src/prerna/reactor/platformprofile/UpdatePlatformProfileReactor.java +++ b/src/prerna/reactor/platformprofile/UpdatePlatformProfileReactor.java @@ -28,15 +28,21 @@ package prerna.reactor.platformprofile; import prerna.auth.User; -import prerna.auth.utils.PlatformProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; +import prerna.sablecc2.om.PixelOperationType; +import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; +/** + * Updates the name and/or description of an existing platform profile. + * + *

Pixel: {@code UpdatePlatformProfile(profileId=[""], name=["New Name"], description=["New description"]);}

+ */ public class UpdatePlatformProfileReactor extends AbstractReactor { public UpdatePlatformProfileReactor() { - this.keysToGet = new String[] { "profileId", "name", "description" }; + this.keysToGet = new String[] { ReactorKeysEnum.PROFILE_ID.getKey(), ReactorKeysEnum.NAME.getKey(), ReactorKeysEnum.DESCRIPTION.getKey() }; this.keyRequired = new int[] { 1, 0, 0 }; } @@ -47,11 +53,11 @@ public NounMetadata execute() { if (!PlatformProfileUtils.canManage(user)) { throw new IllegalArgumentException("User must be an admin to manage platform profiles."); } - String profileId = this.keyValue.get("profileId"); - String name = this.keyValue.get("name"); - String description = this.keyValue.get("description"); + String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); + String name = this.keyValue.get(ReactorKeysEnum.NAME.getKey()); + String description = this.keyValue.get(ReactorKeysEnum.DESCRIPTION.getKey()); PlatformProfileUtils.updateProfile(profileId, name, description, user); - NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Platform profile updated.")); return noun; } diff --git a/src/prerna/util/SystemEngineRegistry.java b/src/prerna/util/SystemEngineRegistry.java index 46ca3f33c56..3f57dd39b1e 100644 --- a/src/prerna/util/SystemEngineRegistry.java +++ b/src/prerna/util/SystemEngineRegistry.java @@ -81,6 +81,7 @@ public final class SystemEngineRegistry { */ private static final Set SECURITY_DB_ALLOWED = Set.of("prerna.auth", "prerna.reactor.security", + "prerna.reactor.appprofile", "prerna.reactor.platformprofile", "prerna.semoss.web.services.config", "prerna.util", "prerna.web.conf"); private static final Set LOCAL_MASTER_DB_ALLOWED = Set.of("prerna.auth", "prerna.masterdatabase", From b57d4e8770e5b0a4a3debac5b22f98098de825f8 Mon Sep 17 00:00:00 2001 From: "Patel, Parth" Date: Fri, 26 Jun 2026 11:21:38 -0400 Subject: [PATCH 7/9] refactor(profiles): reorganize appprofile reactors into sub-packages and convert reads to QS - Move 30 appprofile reactors from flat package into feature/, profile/, subgroup/, manager/, user/ sub-packages for maintainability - Remove legacy alias reactors (AssignUserProfile, RemoveUserProfile, GetUserAppProfile, GetUserFeatures) that duplicated newer equivalents - Refactor AppProfileUtils read methods to use aliased QueryColumnSelector + QueryExecutionUtility.flushRsToMap/flushToInteger, eliminating raw JDBC (ResultSet, WrapperManager, PreparedStatement) from all read paths - Replace correlated subqueries in getProfiles/getSubgroups with LEFT JOIN + GROUP BY + COUNT via QS relations - Convert PlatformProfileUtils reads to same QS + QueryExecutionUtility pattern; replace canManage check with SecurityAdminUtils.userIsAdmin Co-Authored-By: Claude Sonnet 4.6 --- .../reactor/appprofile/AppProfileUtils.java | 800 ++++++------------ .../appprofile/AssignUserProfileReactor.java | 80 -- .../appprofile/GetUserAppProfileReactor.java | 77 -- .../appprofile/GetUserFeaturesReactor.java | 78 -- .../appprofile/RemoveUserProfileReactor.java | 81 -- .../{ => feature}/CheckAppFeatureReactor.java | 5 +- .../{ => feature}/CheckFeatureReactor.java | 5 +- .../CreateAppFeatureReactor.java | 5 +- .../DeleteAppFeatureReactor.java | 5 +- .../{ => feature}/GetAppFeaturesReactor.java | 5 +- .../GetAppUserFeaturesReactor.java | 5 +- .../UpdateAppFeatureReactor.java | 5 +- .../AddAppProfileManagerReactor.java | 5 +- .../GetAppProfileManagersReactor.java | 5 +- .../RemoveAppProfileManagerReactor.java | 5 +- .../CreateAppProfileReactor.java | 5 +- .../DeleteAppProfileReactor.java | 5 +- .../GetAppProfileFeaturesReactor.java | 5 +- .../GetAppProfileUsersReactor.java | 5 +- .../{ => profile}/GetAppProfilesReactor.java | 5 +- .../SetAppProfileFeatureReactor.java | 5 +- .../UpdateAppProfileReactor.java | 5 +- .../AssignAppUserSubgroupReactor.java | 5 +- .../CreateAppSubgroupReactor.java | 5 +- .../DeleteAppSubgroupReactor.java | 5 +- .../GetAppSubgroupFeaturesReactor.java | 5 +- .../GetAppSubgroupUsersReactor.java | 5 +- .../GetAppSubgroupsReactor.java | 5 +- .../RemoveAppUserSubgroupReactor.java | 5 +- .../SetAppSubgroupFeatureReactor.java | 5 +- .../UpdateAppSubgroupReactor.java | 5 +- .../AssignAppUserProfileReactor.java | 5 +- .../{ => user}/GetUserAppProfilesReactor.java | 5 +- .../{ => user}/GetUserProfileReactor.java | 5 +- .../RemoveAppUserProfileReactor.java | 5 +- .../AssignUserPlatformProfileReactor.java | 3 +- .../CreatePlatformProfileReactor.java | 3 +- .../DeletePlatformProfileReactor.java | 3 +- .../GetPlatformFeaturesReactor.java | 3 +- .../GetPlatformProfileUsersReactor.java | 3 +- .../GetPlatformProfilesReactor.java | 3 +- .../platformprofile/PlatformProfileUtils.java | 127 +-- .../RemoveUserPlatformProfileReactor.java | 3 +- .../SetPlatformFeatureReactor.java | 3 +- .../UpdatePlatformProfileReactor.java | 3 +- 45 files changed, 415 insertions(+), 1005 deletions(-) delete mode 100644 src/prerna/reactor/appprofile/AssignUserProfileReactor.java delete mode 100644 src/prerna/reactor/appprofile/GetUserAppProfileReactor.java delete mode 100644 src/prerna/reactor/appprofile/GetUserFeaturesReactor.java delete mode 100644 src/prerna/reactor/appprofile/RemoveUserProfileReactor.java rename src/prerna/reactor/appprofile/{ => feature}/CheckAppFeatureReactor.java (98%) rename src/prerna/reactor/appprofile/{ => feature}/CheckFeatureReactor.java (98%) rename src/prerna/reactor/appprofile/{ => feature}/CreateAppFeatureReactor.java (98%) rename src/prerna/reactor/appprofile/{ => feature}/DeleteAppFeatureReactor.java (98%) rename src/prerna/reactor/appprofile/{ => feature}/GetAppFeaturesReactor.java (98%) rename src/prerna/reactor/appprofile/{ => feature}/GetAppUserFeaturesReactor.java (98%) rename src/prerna/reactor/appprofile/{ => feature}/UpdateAppFeatureReactor.java (98%) rename src/prerna/reactor/appprofile/{ => manager}/AddAppProfileManagerReactor.java (98%) rename src/prerna/reactor/appprofile/{ => manager}/GetAppProfileManagersReactor.java (98%) rename src/prerna/reactor/appprofile/{ => manager}/RemoveAppProfileManagerReactor.java (98%) rename src/prerna/reactor/appprofile/{ => profile}/CreateAppProfileReactor.java (98%) rename src/prerna/reactor/appprofile/{ => profile}/DeleteAppProfileReactor.java (98%) rename src/prerna/reactor/appprofile/{ => profile}/GetAppProfileFeaturesReactor.java (98%) rename src/prerna/reactor/appprofile/{ => profile}/GetAppProfileUsersReactor.java (98%) rename src/prerna/reactor/appprofile/{ => profile}/GetAppProfilesReactor.java (98%) rename src/prerna/reactor/appprofile/{ => profile}/SetAppProfileFeatureReactor.java (98%) rename src/prerna/reactor/appprofile/{ => profile}/UpdateAppProfileReactor.java (98%) rename src/prerna/reactor/appprofile/{ => subgroup}/AssignAppUserSubgroupReactor.java (98%) rename src/prerna/reactor/appprofile/{ => subgroup}/CreateAppSubgroupReactor.java (98%) rename src/prerna/reactor/appprofile/{ => subgroup}/DeleteAppSubgroupReactor.java (98%) rename src/prerna/reactor/appprofile/{ => subgroup}/GetAppSubgroupFeaturesReactor.java (98%) rename src/prerna/reactor/appprofile/{ => subgroup}/GetAppSubgroupUsersReactor.java (98%) rename src/prerna/reactor/appprofile/{ => subgroup}/GetAppSubgroupsReactor.java (98%) rename src/prerna/reactor/appprofile/{ => subgroup}/RemoveAppUserSubgroupReactor.java (98%) rename src/prerna/reactor/appprofile/{ => subgroup}/SetAppSubgroupFeatureReactor.java (98%) rename src/prerna/reactor/appprofile/{ => subgroup}/UpdateAppSubgroupReactor.java (98%) rename src/prerna/reactor/appprofile/{ => user}/AssignAppUserProfileReactor.java (98%) rename src/prerna/reactor/appprofile/{ => user}/GetUserAppProfilesReactor.java (98%) rename src/prerna/reactor/appprofile/{ => user}/GetUserProfileReactor.java (98%) rename src/prerna/reactor/appprofile/{ => user}/RemoveAppUserProfileReactor.java (98%) diff --git a/src/prerna/reactor/appprofile/AppProfileUtils.java b/src/prerna/reactor/appprofile/AppProfileUtils.java index a0b5171b39e..2a0941c8bd8 100644 --- a/src/prerna/reactor/appprofile/AppProfileUtils.java +++ b/src/prerna/reactor/appprofile/AppProfileUtils.java @@ -28,7 +28,6 @@ package prerna.reactor.appprofile; import java.sql.PreparedStatement; -import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; @@ -45,12 +44,14 @@ import prerna.auth.utils.SecurityAdminUtils; import prerna.auth.utils.SecurityProjectUtils; import prerna.engine.api.IRDBMSEngine; -import prerna.engine.api.IRawSelectWrapper; import prerna.query.querystruct.SelectQueryStruct; import prerna.query.querystruct.filters.SimpleQueryFilter; +import prerna.query.querystruct.selectors.QueryColumnOrderBySelector; import prerna.query.querystruct.selectors.QueryColumnSelector; -import prerna.rdf.engine.wrappers.WrapperManager; +import prerna.query.querystruct.selectors.QueryFunctionHelper; +import prerna.query.querystruct.selectors.QueryFunctionSelector; import prerna.util.ConnectionUtils; +import prerna.util.QueryExecutionUtility; import prerna.util.SystemEngineRegistry; import prerna.util.Utility; @@ -88,21 +89,16 @@ public static boolean canAssignProfiles(User user, String appId) { String userId = getUserId(user); if (userId == null) return false; IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - String sql = "SELECT COUNT(*) FROM APP_PROFILE_MANAGER WHERE APP_ID=? AND USER_ID=? AND PERMISSION='assign'"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(sql); - ps.setString(1, appId); - ps.setString(2, userId); - rs = ps.executeQuery(); - if (rs.next()) return rs.getInt(1) > 0; - } catch (SQLException e) { - classLogger.error("Failed to check assign permission", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); - } - return false; + SelectQueryStruct qs = new SelectQueryStruct(); + QueryFunctionSelector countFn = new QueryFunctionSelector(); + countFn.addInnerSelector(new QueryColumnSelector("APP_PROFILE_MANAGER__USER_ID")); + countFn.setFunction(QueryFunctionHelper.COUNT); + qs.addSelector(countFn); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE_MANAGER__APP_ID", "==", appId)); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE_MANAGER__USER_ID", "==", userId)); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE_MANAGER__PERMISSION", "==", "assign")); + Integer count = QueryExecutionUtility.flushToInteger(securityDb, qs); + return count != null && count > 0; } /** @@ -118,14 +114,9 @@ public static boolean canEvaluateFeatures(User user, String appId) { private static boolean appExists(String appId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); SelectQueryStruct qs = new SelectQueryStruct(); - qs.addSelector(new QueryColumnSelector("PROJECT__PROJECTID")); + qs.addSelector(new QueryColumnSelector("PROJECT__PROJECTID", "projectId")); qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("PROJECT__PROJECTID", "==", appId)); - try (IRawSelectWrapper wrapper = WrapperManager.getInstance().getRawWrapper(securityDb, qs)) { - return wrapper.hasNext(); - } catch (Exception e) { - classLogger.error("Error checking app existence", e); - return false; - } + return QueryExecutionUtility.flushToString(securityDb, qs) != null; } // ─── Profile CRUD ─────────────────────────────────────────────────────── @@ -289,38 +280,34 @@ public static void deleteProfile(String appId, String profileId, User user) { /** * Returns all profiles for an app, each with a live USER_COUNT from - * APP_USER_PROFILE. Uses a correlated subquery and must remain as - * PreparedStatement. + * APP_USER_PROFILE via LEFT JOIN + GROUP BY. */ public static List> getProfiles(String appId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - List> profiles = new ArrayList<>(); - String sql = "SELECT p.PROFILE_ID, p.PROFILE_NAME, p.DESCRIPTION, p.IS_DEFAULT, p.IS_GROUP, p.CREATED_BY, p.CREATED_AT, " - + "(SELECT COUNT(DISTINCT up.USER_ID) FROM APP_USER_PROFILE up WHERE up.APP_ID=p.APP_ID AND up.PROFILE_ID=p.PROFILE_ID) AS USER_COUNT " - + "FROM APP_PROFILE p WHERE p.APP_ID=? ORDER BY p.PROFILE_NAME ASC"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(sql); - ps.setString(1, appId); - rs = ps.executeQuery(); - while (rs.next()) { - Map profile = new HashMap<>(); - profile.put("profileId", rs.getString("PROFILE_ID")); - profile.put("profileName", rs.getString("PROFILE_NAME")); - profile.put("description", rs.getString("DESCRIPTION")); - profile.put("isDefault", rs.getBoolean("IS_DEFAULT")); - profile.put("isGroup", rs.getBoolean("IS_GROUP")); - profile.put("createdBy", rs.getString("CREATED_BY")); - profile.put("createdAt", rs.getTimestamp("CREATED_AT")); - profile.put("userCount", rs.getInt("USER_COUNT")); - profiles.add(profile); - } - } catch (SQLException e) { - classLogger.error("Failed to get app profiles", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); - } + SelectQueryStruct qs = new SelectQueryStruct(); + qs.addSelector(new QueryColumnSelector("APP_PROFILE__PROFILE_ID", "profileId")); + qs.addSelector(new QueryColumnSelector("APP_PROFILE__PROFILE_NAME", "profileName")); + qs.addSelector(new QueryColumnSelector("APP_PROFILE__DESCRIPTION", "description")); + qs.addSelector(new QueryColumnSelector("APP_PROFILE__IS_DEFAULT", "isDefault")); + qs.addSelector(new QueryColumnSelector("APP_PROFILE__IS_GROUP", "isGroup")); + qs.addSelector(new QueryColumnSelector("APP_PROFILE__CREATED_BY", "createdBy")); + qs.addSelector(new QueryColumnSelector("APP_PROFILE__CREATED_AT", "createdAt")); + qs.addSelector(QueryFunctionSelector.makeFunctionSelector(QueryFunctionHelper.COUNT, "APP_USER_PROFILE__USER_ID", "userCount")); + qs.addRelation("APP_PROFILE__PROFILE_ID", "APP_USER_PROFILE__PROFILE_ID", "left.join"); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE__APP_ID", "==", appId)); + qs.addGroupBy(new QueryColumnSelector("APP_PROFILE__PROFILE_ID")); + qs.addGroupBy(new QueryColumnSelector("APP_PROFILE__PROFILE_NAME")); + qs.addGroupBy(new QueryColumnSelector("APP_PROFILE__DESCRIPTION")); + qs.addGroupBy(new QueryColumnSelector("APP_PROFILE__IS_DEFAULT")); + qs.addGroupBy(new QueryColumnSelector("APP_PROFILE__IS_GROUP")); + qs.addGroupBy(new QueryColumnSelector("APP_PROFILE__CREATED_BY")); + qs.addGroupBy(new QueryColumnSelector("APP_PROFILE__CREATED_AT")); + qs.addOrderBy(new QueryColumnOrderBySelector("APP_PROFILE__PROFILE_NAME")); + List> profiles = QueryExecutionUtility.flushRsToMap(securityDb, qs); + profiles.forEach(p -> { + p.put("isDefault", Boolean.TRUE.equals(p.get("isDefault"))); + p.put("isGroup", Boolean.TRUE.equals(p.get("isGroup"))); + }); return profiles; } @@ -443,32 +430,15 @@ public static void deleteFeature(String appId, String featureId, User user) { */ public static List> getFeatures(String appId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - List> features = new ArrayList<>(); - - SelectQueryStruct sqs = new SelectQueryStruct(); - sqs.addSelector(new QueryColumnSelector("APP_FEATURE__FEATURE_ID")); - sqs.addSelector(new QueryColumnSelector("APP_FEATURE__FEATURE_KEY")); - sqs.addSelector(new QueryColumnSelector("APP_FEATURE__DESCRIPTION")); - sqs.addSelector(new QueryColumnSelector("APP_FEATURE__CREATED_BY")); - sqs.addSelector(new QueryColumnSelector("APP_FEATURE__CREATED_AT")); - sqs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_FEATURE__APP_ID", "==", appId)); - sqs.addOrderBy("APP_FEATURE__FEATURE_KEY", "ASC"); - - try (IRawSelectWrapper wrapper = WrapperManager.getInstance().getRawWrapper(securityDb, sqs)) { - while (wrapper.hasNext()) { - Object[] values = wrapper.next().getValues(); - Map feature = new HashMap<>(); - feature.put("featureId", values[0]); - feature.put("featureKey", values[1]); - feature.put("description", values[2]); - feature.put("createdBy", values[3]); - feature.put("createdAt", values[4]); - features.add(feature); - } - } catch (Exception e) { - classLogger.error("Failed to get app features", e); - } - return features; + SelectQueryStruct qs = new SelectQueryStruct(); + qs.addSelector(new QueryColumnSelector("APP_FEATURE__FEATURE_ID", "featureId")); + qs.addSelector(new QueryColumnSelector("APP_FEATURE__FEATURE_KEY", "featureKey")); + qs.addSelector(new QueryColumnSelector("APP_FEATURE__DESCRIPTION", "description")); + qs.addSelector(new QueryColumnSelector("APP_FEATURE__CREATED_BY", "createdBy")); + qs.addSelector(new QueryColumnSelector("APP_FEATURE__CREATED_AT", "createdAt")); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_FEATURE__APP_ID", "==", appId)); + qs.addOrderBy(new QueryColumnOrderBySelector("APP_FEATURE__FEATURE_KEY")); + return QueryExecutionUtility.flushRsToMap(securityDb, qs); } // ─── Profile-Feature Assignment ───────────────────────────────────────── @@ -513,52 +483,27 @@ public static void setProfileFeature(String appId, String profileId, String feat /** * Returns all features for an app merged with their enabled state for the given - * profile. Uses two sequential queries joined in code; kept as PreparedStatement. + * profile. Defaults to false for features without an explicit assignment. */ public static List> getProfileFeatures(String appId, String profileId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - List> results = new ArrayList<>(); + SelectQueryStruct qs1 = new SelectQueryStruct(); + qs1.addSelector(new QueryColumnSelector("APP_PROFILE_FEATURE__FEATURE_ID", "featureId")); + qs1.addSelector(new QueryColumnSelector("APP_PROFILE_FEATURE__ENABLED", "enabled")); + qs1.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE_FEATURE__APP_ID", "==", appId)); + qs1.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE_FEATURE__PROFILE_ID", "==", profileId)); Map enabledMap = new HashMap<>(); - String assignSql = "SELECT FEATURE_ID, ENABLED FROM APP_PROFILE_FEATURE " - + "WHERE APP_ID=? AND PROFILE_ID=?"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(assignSql); - ps.setString(1, appId); - ps.setString(2, profileId); - rs = ps.executeQuery(); - while (rs.next()) { - enabledMap.put(rs.getString("FEATURE_ID"), rs.getBoolean("ENABLED")); - } - } catch (SQLException e) { - classLogger.error("Failed to get profile feature assignments", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); - } - - String featSql = "SELECT FEATURE_ID, FEATURE_KEY, DESCRIPTION " - + "FROM APP_FEATURE WHERE APP_ID=? ORDER BY FEATURE_KEY ASC"; - ps = null; - rs = null; - try { - ps = securityDb.getPreparedStatement(featSql); - ps.setString(1, appId); - rs = ps.executeQuery(); - while (rs.next()) { - String featureId = rs.getString("FEATURE_ID"); - Map row = new HashMap<>(); - row.put("featureId", featureId); - row.put("featureKey", rs.getString("FEATURE_KEY")); - row.put("description", rs.getString("DESCRIPTION")); - row.put("enabled", enabledMap.getOrDefault(featureId, Boolean.FALSE)); - results.add(row); - } - } catch (SQLException e) { - classLogger.error("Failed to get profile features", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); - } + for (Map r : QueryExecutionUtility.flushRsToMap(securityDb, qs1)) { + enabledMap.put((String) r.get("featureId"), Boolean.TRUE.equals(r.get("enabled"))); + } + SelectQueryStruct qs2 = new SelectQueryStruct(); + qs2.addSelector(new QueryColumnSelector("APP_FEATURE__FEATURE_ID", "featureId")); + qs2.addSelector(new QueryColumnSelector("APP_FEATURE__FEATURE_KEY", "featureKey")); + qs2.addSelector(new QueryColumnSelector("APP_FEATURE__DESCRIPTION", "description")); + qs2.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_FEATURE__APP_ID", "==", appId)); + qs2.addOrderBy(new QueryColumnOrderBySelector("APP_FEATURE__FEATURE_KEY")); + List> results = QueryExecutionUtility.flushRsToMap(securityDb, qs2); + results.forEach(r -> r.put("enabled", enabledMap.getOrDefault((String) r.get("featureId"), Boolean.FALSE))); return results; } @@ -571,26 +516,19 @@ public static List> getProfileFeatures(String appId, String public static void assignUserProfile(String appId, String userId, String profileId, User actor) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); // check for existing assignment to avoid duplicates - String checkSql = "SELECT COUNT(*) FROM APP_USER_PROFILE WHERE APP_ID=? AND USER_ID=? AND PROFILE_ID=?"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(checkSql); - ps.setString(1, appId); - ps.setString(2, userId); - ps.setString(3, profileId); - rs = ps.executeQuery(); - if (rs.next() && rs.getInt(1) > 0) { - return; // already assigned - } - } catch (SQLException e) { - classLogger.error("Failed to check existing profile assignment", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); - } + SelectQueryStruct checkQs = new SelectQueryStruct(); + QueryFunctionSelector countFn = new QueryFunctionSelector(); + countFn.addInnerSelector(new QueryColumnSelector("APP_USER_PROFILE__USER_ID")); + countFn.setFunction(QueryFunctionHelper.COUNT); + checkQs.addSelector(countFn); + checkQs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_PROFILE__APP_ID", "==", appId)); + checkQs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_PROFILE__USER_ID", "==", userId)); + checkQs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_PROFILE__PROFILE_ID", "==", profileId)); + Integer existingCount = QueryExecutionUtility.flushToInteger(securityDb, checkQs); + if (existingCount != null && existingCount > 0) return; String actorId = getUserId(actor); - ps = null; + PreparedStatement ps = null; try { ps = securityDb.getPreparedStatement( "INSERT INTO APP_USER_PROFILE (APP_ID, USER_ID, PROFILE_ID, ASSIGNED_BY, ASSIGNED_AT) VALUES (?,?,?,?,?)"); @@ -726,37 +664,20 @@ public static Map getUserAppProfiles(String appId, User user) { /** * Returns all users assigned to a profile, including display name and email via - * JOIN with SMSS_USER. Kept as PreparedStatement due to the LEFT JOIN. + * JOIN with SMSS_USER. */ public static List> getProfileUsers(String appId, String profileId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - List> users = new ArrayList<>(); - String sql = "SELECT up.USER_ID, u.NAME, u.EMAIL, up.ASSIGNED_BY, up.ASSIGNED_AT " - + "FROM APP_USER_PROFILE up " - + "LEFT JOIN SMSS_USER u ON up.USER_ID = u.ID " - + "WHERE up.APP_ID = ? AND up.PROFILE_ID = ?"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(sql); - ps.setString(1, appId); - ps.setString(2, profileId); - rs = ps.executeQuery(); - while (rs.next()) { - Map row = new HashMap<>(); - row.put("userId", rs.getString("USER_ID")); - row.put("name", rs.getString("NAME")); - row.put("email", rs.getString("EMAIL")); - row.put("assignedBy", rs.getString("ASSIGNED_BY")); - row.put("assignedAt", rs.getTimestamp("ASSIGNED_AT")); - users.add(row); - } - } catch (SQLException e) { - classLogger.error("Failed to get profile users", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); - } - return users; + SelectQueryStruct qs = new SelectQueryStruct(); + qs.addSelector(new QueryColumnSelector("APP_USER_PROFILE__USER_ID", "userId")); + qs.addSelector(new QueryColumnSelector("SMSS_USER__NAME", "name")); + qs.addSelector(new QueryColumnSelector("SMSS_USER__EMAIL", "email")); + qs.addSelector(new QueryColumnSelector("APP_USER_PROFILE__ASSIGNED_BY", "assignedBy")); + qs.addSelector(new QueryColumnSelector("APP_USER_PROFILE__ASSIGNED_AT", "assignedAt")); + qs.addRelation("APP_USER_PROFILE__USER_ID", "SMSS_USER__ID", "left.join"); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_PROFILE__APP_ID", "==", appId)); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_PROFILE__PROFILE_ID", "==", profileId)); + return QueryExecutionUtility.flushRsToMap(securityDb, qs); } // ─── Subgroup CRUD ─────────────────────────────────────────────────────── @@ -866,72 +787,45 @@ public static void deleteSubgroup(String appId, String subgroupId, User user) { /** * Returns all sub-groups for a profile, each with a live USER_COUNT from - * APP_USER_SUBGROUP. Uses a correlated subquery; kept as PreparedStatement. + * APP_USER_SUBGROUP via LEFT JOIN + GROUP BY. */ public static List> getSubgroups(String appId, String profileId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - List> result = new ArrayList<>(); - String sql = "SELECT s.SUBGROUP_ID, s.SUBGROUP_NAME, s.DESCRIPTION, s.CREATED_BY, s.CREATED_AT, " - + "(SELECT COUNT(*) FROM APP_USER_SUBGROUP us WHERE us.APP_ID=s.APP_ID AND us.SUBGROUP_ID=s.SUBGROUP_ID) AS USER_COUNT " - + "FROM APP_PROFILE_SUBGROUP s WHERE s.APP_ID=? AND s.PROFILE_ID=? ORDER BY s.SUBGROUP_NAME ASC"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(sql); - ps.setString(1, appId); - ps.setString(2, profileId); - rs = ps.executeQuery(); - while (rs.next()) { - Map row = new HashMap<>(); - row.put("subgroupId", rs.getString("SUBGROUP_ID")); - row.put("subgroupName", rs.getString("SUBGROUP_NAME")); - row.put("description", rs.getString("DESCRIPTION")); - row.put("createdBy", rs.getString("CREATED_BY")); - row.put("createdAt", rs.getTimestamp("CREATED_AT")); - row.put("userCount", rs.getInt("USER_COUNT")); - result.add(row); - } - } catch (SQLException e) { - classLogger.error("Failed to get subgroups", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); - } - return result; + SelectQueryStruct qs = new SelectQueryStruct(); + qs.addSelector(new QueryColumnSelector("APP_PROFILE_SUBGROUP__SUBGROUP_ID", "subgroupId")); + qs.addSelector(new QueryColumnSelector("APP_PROFILE_SUBGROUP__SUBGROUP_NAME", "subgroupName")); + qs.addSelector(new QueryColumnSelector("APP_PROFILE_SUBGROUP__DESCRIPTION", "description")); + qs.addSelector(new QueryColumnSelector("APP_PROFILE_SUBGROUP__CREATED_BY", "createdBy")); + qs.addSelector(new QueryColumnSelector("APP_PROFILE_SUBGROUP__CREATED_AT", "createdAt")); + qs.addSelector(QueryFunctionSelector.makeFunctionSelector(QueryFunctionHelper.COUNT, "APP_USER_SUBGROUP__USER_ID", "userCount")); + qs.addRelation("APP_PROFILE_SUBGROUP__SUBGROUP_ID", "APP_USER_SUBGROUP__SUBGROUP_ID", "left.join"); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE_SUBGROUP__APP_ID", "==", appId)); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE_SUBGROUP__PROFILE_ID", "==", profileId)); + qs.addGroupBy(new QueryColumnSelector("APP_PROFILE_SUBGROUP__SUBGROUP_ID")); + qs.addGroupBy(new QueryColumnSelector("APP_PROFILE_SUBGROUP__SUBGROUP_NAME")); + qs.addGroupBy(new QueryColumnSelector("APP_PROFILE_SUBGROUP__DESCRIPTION")); + qs.addGroupBy(new QueryColumnSelector("APP_PROFILE_SUBGROUP__CREATED_BY")); + qs.addGroupBy(new QueryColumnSelector("APP_PROFILE_SUBGROUP__CREATED_AT")); + qs.addOrderBy(new QueryColumnOrderBySelector("APP_PROFILE_SUBGROUP__SUBGROUP_NAME")); + return QueryExecutionUtility.flushRsToMap(securityDb, qs); } /** * Returns all users assigned to a sub-group, including display name and email - * via JOIN with SMSS_USER. Kept as PreparedStatement due to the LEFT JOIN. + * via JOIN with SMSS_USER. */ public static List> getSubgroupUsers(String appId, String subgroupId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - List> users = new ArrayList<>(); - String sql = "SELECT us.USER_ID, u.NAME, u.EMAIL, us.ASSIGNED_BY, us.ASSIGNED_AT " - + "FROM APP_USER_SUBGROUP us " - + "LEFT JOIN SMSS_USER u ON us.USER_ID = u.ID " - + "WHERE us.APP_ID=? AND us.SUBGROUP_ID=?"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(sql); - ps.setString(1, appId); - ps.setString(2, subgroupId); - rs = ps.executeQuery(); - while (rs.next()) { - Map row = new HashMap<>(); - row.put("userId", rs.getString("USER_ID")); - row.put("name", rs.getString("NAME")); - row.put("email", rs.getString("EMAIL")); - row.put("assignedBy", rs.getString("ASSIGNED_BY")); - row.put("assignedAt", rs.getTimestamp("ASSIGNED_AT")); - users.add(row); - } - } catch (SQLException e) { - classLogger.error("Failed to get subgroup users", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); - } - return users; + SelectQueryStruct qs = new SelectQueryStruct(); + qs.addSelector(new QueryColumnSelector("APP_USER_SUBGROUP__USER_ID", "userId")); + qs.addSelector(new QueryColumnSelector("SMSS_USER__NAME", "name")); + qs.addSelector(new QueryColumnSelector("SMSS_USER__EMAIL", "email")); + qs.addSelector(new QueryColumnSelector("APP_USER_SUBGROUP__ASSIGNED_BY", "assignedBy")); + qs.addSelector(new QueryColumnSelector("APP_USER_SUBGROUP__ASSIGNED_AT", "assignedAt")); + qs.addRelation("APP_USER_SUBGROUP__USER_ID", "SMSS_USER__ID", "left.join"); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_SUBGROUP__APP_ID", "==", appId)); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_SUBGROUP__SUBGROUP_ID", "==", subgroupId)); + return QueryExecutionUtility.flushRsToMap(securityDb, qs); } // ─── Subgroup-Feature Assignment ──────────────────────────────────────── @@ -977,50 +871,27 @@ public static void setSubgroupFeature(String appId, String subgroupId, String fe /** * Returns all features for an app merged with their enabled state for the given - * sub-group. Uses two sequential queries joined in code; kept as PreparedStatement. + * sub-group. Defaults to false for features without an explicit assignment. */ public static List> getSubgroupFeatures(String appId, String subgroupId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - List> results = new ArrayList<>(); + SelectQueryStruct qs1 = new SelectQueryStruct(); + qs1.addSelector(new QueryColumnSelector("APP_SUBGROUP_FEATURE__FEATURE_ID", "featureId")); + qs1.addSelector(new QueryColumnSelector("APP_SUBGROUP_FEATURE__ENABLED", "enabled")); + qs1.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_SUBGROUP_FEATURE__APP_ID", "==", appId)); + qs1.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_SUBGROUP_FEATURE__SUBGROUP_ID", "==", subgroupId)); Map enabledMap = new HashMap<>(); - String assignSql = "SELECT FEATURE_ID, ENABLED FROM APP_SUBGROUP_FEATURE WHERE APP_ID=? AND SUBGROUP_ID=?"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(assignSql); - ps.setString(1, appId); - ps.setString(2, subgroupId); - rs = ps.executeQuery(); - while (rs.next()) { - enabledMap.put(rs.getString("FEATURE_ID"), rs.getBoolean("ENABLED")); - } - } catch (SQLException e) { - classLogger.error("Failed to get subgroup feature assignments", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); - } - - String featSql = "SELECT FEATURE_ID, FEATURE_KEY, DESCRIPTION FROM APP_FEATURE WHERE APP_ID=? ORDER BY FEATURE_KEY ASC"; - ps = null; - rs = null; - try { - ps = securityDb.getPreparedStatement(featSql); - ps.setString(1, appId); - rs = ps.executeQuery(); - while (rs.next()) { - String featureId = rs.getString("FEATURE_ID"); - Map row = new HashMap<>(); - row.put("featureId", featureId); - row.put("featureKey", rs.getString("FEATURE_KEY")); - row.put("description", rs.getString("DESCRIPTION")); - row.put("enabled", enabledMap.getOrDefault(featureId, Boolean.FALSE)); - results.add(row); - } - } catch (SQLException e) { - classLogger.error("Failed to get subgroup features", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); - } + for (Map r : QueryExecutionUtility.flushRsToMap(securityDb, qs1)) { + enabledMap.put((String) r.get("featureId"), Boolean.TRUE.equals(r.get("enabled"))); + } + SelectQueryStruct qs2 = new SelectQueryStruct(); + qs2.addSelector(new QueryColumnSelector("APP_FEATURE__FEATURE_ID", "featureId")); + qs2.addSelector(new QueryColumnSelector("APP_FEATURE__FEATURE_KEY", "featureKey")); + qs2.addSelector(new QueryColumnSelector("APP_FEATURE__DESCRIPTION", "description")); + qs2.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_FEATURE__APP_ID", "==", appId)); + qs2.addOrderBy(new QueryColumnOrderBySelector("APP_FEATURE__FEATURE_KEY")); + List> results = QueryExecutionUtility.flushRsToMap(securityDb, qs2); + results.forEach(r -> r.put("enabled", enabledMap.getOrDefault((String) r.get("featureId"), Boolean.FALSE))); return results; } @@ -1032,24 +903,19 @@ public static List> getSubgroupFeatures(String appId, String public static void assignUserSubgroup(String appId, String userId, String subgroupId, User actor) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); // check for existing assignment - String checkSql = "SELECT COUNT(*) FROM APP_USER_SUBGROUP WHERE APP_ID=? AND USER_ID=? AND SUBGROUP_ID=?"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(checkSql); - ps.setString(1, appId); - ps.setString(2, userId); - ps.setString(3, subgroupId); - rs = ps.executeQuery(); - if (rs.next() && rs.getInt(1) > 0) return; - } catch (SQLException e) { - classLogger.error("Failed to check existing subgroup assignment", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); - } + SelectQueryStruct checkQs = new SelectQueryStruct(); + QueryFunctionSelector countFn = new QueryFunctionSelector(); + countFn.addInnerSelector(new QueryColumnSelector("APP_USER_SUBGROUP__USER_ID")); + countFn.setFunction(QueryFunctionHelper.COUNT); + checkQs.addSelector(countFn); + checkQs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_SUBGROUP__APP_ID", "==", appId)); + checkQs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_SUBGROUP__USER_ID", "==", userId)); + checkQs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_SUBGROUP__SUBGROUP_ID", "==", subgroupId)); + Integer existingCount = QueryExecutionUtility.flushToInteger(securityDb, checkQs); + if (existingCount != null && existingCount > 0) return; String actorId = getUserId(actor); - ps = null; + PreparedStatement ps = null; try { ps = securityDb.getPreparedStatement( "INSERT INTO APP_USER_SUBGROUP (APP_ID, USER_ID, SUBGROUP_ID, ASSIGNED_BY, ASSIGNED_AT) VALUES (?,?,?,?,?)"); @@ -1098,22 +964,17 @@ public static void removeUserSubgroup(String appId, String userId, String subgro public static void addProfileManager(String appId, String userId, User actor) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); // check for existing - String checkSql = "SELECT COUNT(*) FROM APP_PROFILE_MANAGER WHERE APP_ID=? AND USER_ID=?"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(checkSql); - ps.setString(1, appId); - ps.setString(2, userId); - rs = ps.executeQuery(); - if (rs.next() && rs.getInt(1) > 0) return; - } catch (SQLException e) { - classLogger.error("Failed to check existing profile manager", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); - } + SelectQueryStruct checkQs = new SelectQueryStruct(); + QueryFunctionSelector countFn = new QueryFunctionSelector(); + countFn.addInnerSelector(new QueryColumnSelector("APP_PROFILE_MANAGER__USER_ID")); + countFn.setFunction(QueryFunctionHelper.COUNT); + checkQs.addSelector(countFn); + checkQs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE_MANAGER__APP_ID", "==", appId)); + checkQs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE_MANAGER__USER_ID", "==", userId)); + Integer existingCount = QueryExecutionUtility.flushToInteger(securityDb, checkQs); + if (existingCount != null && existingCount > 0) return; - ps = null; + PreparedStatement ps = null; try { ps = securityDb.getPreparedStatement( "INSERT INTO APP_PROFILE_MANAGER (APP_ID, USER_ID, PERMISSION) VALUES (?,?,'assign')"); @@ -1150,35 +1011,18 @@ public static void removeProfileManager(String appId, String userId) { /** * Returns all users with delegated profile manager permission for an app, with - * display name and email via JOIN. Kept as PreparedStatement due to LEFT JOIN. + * display name and email via JOIN with SMSS_USER. */ public static List> getProfileManagers(String appId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - List> result = new ArrayList<>(); - String sql = "SELECT pm.USER_ID, u.NAME, u.EMAIL, pm.PERMISSION " - + "FROM APP_PROFILE_MANAGER pm " - + "LEFT JOIN SMSS_USER u ON pm.USER_ID = u.ID " - + "WHERE pm.APP_ID=?"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(sql); - ps.setString(1, appId); - rs = ps.executeQuery(); - while (rs.next()) { - Map row = new HashMap<>(); - row.put("userId", rs.getString("USER_ID")); - row.put("name", rs.getString("NAME")); - row.put("email", rs.getString("EMAIL")); - row.put("permission", rs.getString("PERMISSION")); - result.add(row); - } - } catch (SQLException e) { - classLogger.error("Failed to get profile managers", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); - } - return result; + SelectQueryStruct qs = new SelectQueryStruct(); + qs.addSelector(new QueryColumnSelector("APP_PROFILE_MANAGER__USER_ID", "userId")); + qs.addSelector(new QueryColumnSelector("SMSS_USER__NAME", "name")); + qs.addSelector(new QueryColumnSelector("SMSS_USER__EMAIL", "email")); + qs.addSelector(new QueryColumnSelector("APP_PROFILE_MANAGER__PERMISSION", "permission")); + qs.addRelation("APP_PROFILE_MANAGER__USER_ID", "SMSS_USER__ID", "left.join"); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE_MANAGER__APP_ID", "==", appId)); + return QueryExecutionUtility.flushRsToMap(securityDb, qs); } // ─── Feature evaluation ────────────────────────────────────────────────── @@ -1311,47 +1155,31 @@ private static void clearDefaultProfile(IRDBMSEngine securityDb, String appId) { } private static int getAssignedUserCount(IRDBMSEngine securityDb, String appId, String profileId) { - // Count distinct users in APP_USER_PROFILE for this profile - String sql = "SELECT COUNT(DISTINCT USER_ID) FROM APP_USER_PROFILE WHERE APP_ID=? AND PROFILE_ID=?"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(sql); - ps.setString(1, appId); - ps.setString(2, profileId); - rs = ps.executeQuery(); - if (rs.next()) return rs.getInt(1); - } catch (SQLException e) { - classLogger.error("Failed to count profile users", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); - } - return 0; + SelectQueryStruct qs = new SelectQueryStruct(); + QueryFunctionSelector countFn = new QueryFunctionSelector(); + countFn.addInnerSelector(new QueryColumnSelector("APP_USER_PROFILE__USER_ID")); + countFn.setFunction(QueryFunctionHelper.COUNT); + qs.addSelector(countFn); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_PROFILE__APP_ID", "==", appId)); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_PROFILE__PROFILE_ID", "==", profileId)); + Integer count = QueryExecutionUtility.flushToInteger(securityDb, qs); + return count != null ? count : 0; } private static Map getDefaultProfile(IRDBMSEngine securityDb, String appId) { - String sql = "SELECT PROFILE_ID, PROFILE_NAME, IS_GROUP FROM APP_PROFILE WHERE APP_ID=? AND IS_DEFAULT=TRUE"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(sql); - ps.setString(1, appId); - rs = ps.executeQuery(); - if (rs.next()) { - Map result = new HashMap<>(); - result.put("profileId", rs.getString("PROFILE_ID")); - result.put("profileName", rs.getString("PROFILE_NAME")); - result.put("isGroup", rs.getBoolean("IS_GROUP")); - result.put("isExplicitAssignment", false); - return result; - } - } catch (SQLException e) { - classLogger.error("Failed to get default profile", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); - } - return null; + SelectQueryStruct qs = new SelectQueryStruct(); + qs.addSelector(new QueryColumnSelector("APP_PROFILE__PROFILE_ID", "profileId")); + qs.addSelector(new QueryColumnSelector("APP_PROFILE__PROFILE_NAME", "profileName")); + qs.addSelector(new QueryColumnSelector("APP_PROFILE__IS_GROUP", "isGroup")); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE__APP_ID", "==", appId)); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE__IS_DEFAULT", "==", Boolean.TRUE)); + List> rows = QueryExecutionUtility.flushRsToMap(securityDb, qs); + if (rows.isEmpty()) return null; + Map row = rows.get(0); + row.put("isGroup", Boolean.TRUE.equals(row.get("isGroup"))); + row.put("isExplicitAssignment", false); + return row; } /** @@ -1359,30 +1187,15 @@ private static Map getDefaultProfile(IRDBMSEngine securityDb, St */ private static List> getExplicitUserProfiles(IRDBMSEngine securityDb, String appId, String userId) { - List> profiles = new ArrayList<>(); - String sql = "SELECT p.PROFILE_ID, p.PROFILE_NAME, p.IS_GROUP " - + "FROM APP_USER_PROFILE up " - + "JOIN APP_PROFILE p ON up.PROFILE_ID = p.PROFILE_ID AND up.APP_ID = p.APP_ID " - + "WHERE up.APP_ID=? AND up.USER_ID=?"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(sql); - ps.setString(1, appId); - ps.setString(2, userId); - rs = ps.executeQuery(); - while (rs.next()) { - Map row = new HashMap<>(); - row.put("profileId", rs.getString("PROFILE_ID")); - row.put("profileName", rs.getString("PROFILE_NAME")); - row.put("isGroup", rs.getBoolean("IS_GROUP")); - profiles.add(row); - } - } catch (SQLException e) { - classLogger.error("Failed to get explicit user profiles", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); - } + SelectQueryStruct qs = new SelectQueryStruct(); + qs.addSelector(new QueryColumnSelector("APP_PROFILE__PROFILE_ID", "profileId")); + qs.addSelector(new QueryColumnSelector("APP_PROFILE__PROFILE_NAME", "profileName")); + qs.addSelector(new QueryColumnSelector("APP_PROFILE__IS_GROUP", "isGroup")); + qs.addRelation("APP_USER_PROFILE__PROFILE_ID", "APP_PROFILE__PROFILE_ID", "inner.join"); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_PROFILE__APP_ID", "==", appId)); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_PROFILE__USER_ID", "==", userId)); + List> profiles = QueryExecutionUtility.flushRsToMap(securityDb, qs); + profiles.forEach(r -> r.put("isGroup", Boolean.TRUE.equals(r.get("isGroup")))); return profiles; } @@ -1391,190 +1204,111 @@ private static List> getExplicitUserProfiles(IRDBMSEngine se */ private static List> getExplicitUserSubgroups(IRDBMSEngine securityDb, String appId, String userId) { - List> subgroups = new ArrayList<>(); - String sql = "SELECT us.SUBGROUP_ID, sg.SUBGROUP_NAME, p.PROFILE_ID, p.PROFILE_NAME " - + "FROM APP_USER_SUBGROUP us " - + "JOIN APP_PROFILE_SUBGROUP sg ON us.SUBGROUP_ID = sg.SUBGROUP_ID " - + "JOIN APP_PROFILE p ON sg.PROFILE_ID = p.PROFILE_ID " - + "WHERE us.APP_ID=? AND us.USER_ID=?"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(sql); - ps.setString(1, appId); - ps.setString(2, userId); - rs = ps.executeQuery(); - while (rs.next()) { - Map row = new HashMap<>(); - row.put("subgroupId", rs.getString("SUBGROUP_ID")); - row.put("subgroupName", rs.getString("SUBGROUP_NAME")); - row.put("profileId", rs.getString("PROFILE_ID")); - row.put("profileName", rs.getString("PROFILE_NAME")); - subgroups.add(row); - } - } catch (SQLException e) { - classLogger.error("Failed to get explicit user subgroups", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); - } - return subgroups; + SelectQueryStruct qs = new SelectQueryStruct(); + qs.addSelector(new QueryColumnSelector("APP_USER_SUBGROUP__SUBGROUP_ID", "subgroupId")); + qs.addSelector(new QueryColumnSelector("APP_PROFILE_SUBGROUP__SUBGROUP_NAME", "subgroupName")); + qs.addSelector(new QueryColumnSelector("APP_PROFILE__PROFILE_ID", "profileId")); + qs.addSelector(new QueryColumnSelector("APP_PROFILE__PROFILE_NAME", "profileName")); + qs.addRelation("APP_USER_SUBGROUP__SUBGROUP_ID", "APP_PROFILE_SUBGROUP__SUBGROUP_ID", "inner.join"); + qs.addRelation("APP_PROFILE_SUBGROUP__PROFILE_ID", "APP_PROFILE__PROFILE_ID", "inner.join"); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_SUBGROUP__APP_ID", "==", appId)); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_SUBGROUP__USER_ID", "==", userId)); + return QueryExecutionUtility.flushRsToMap(securityDb, qs); } private static void addProfileFeaturesToResult(IRDBMSEngine securityDb, String appId, String profileId, String profileName, boolean isDefaultProfile, Map result) { - String sql = "SELECT f.FEATURE_KEY, f.FEATURE_ID " - + "FROM APP_PROFILE_FEATURE pf " - + "JOIN APP_FEATURE f ON pf.FEATURE_ID = f.FEATURE_ID AND pf.APP_ID = f.APP_ID " - + "WHERE pf.APP_ID=? AND pf.PROFILE_ID=? AND pf.ENABLED=TRUE"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(sql); - ps.setString(1, appId); - ps.setString(2, profileId); - rs = ps.executeQuery(); - while (rs.next()) { - String featureKey = rs.getString("FEATURE_KEY"); - if (!result.containsKey(featureKey)) { - Map featureInfo = new HashMap<>(); - featureInfo.put("featureId", rs.getString("FEATURE_ID")); - featureInfo.put("profileName", profileName); - featureInfo.put("isDefaultProfile", isDefaultProfile); - result.put(featureKey, featureInfo); - } + SelectQueryStruct qs = new SelectQueryStruct(); + qs.addSelector(new QueryColumnSelector("APP_FEATURE__FEATURE_KEY", "featureKey")); + qs.addSelector(new QueryColumnSelector("APP_FEATURE__FEATURE_ID", "featureId")); + qs.addRelation("APP_PROFILE_FEATURE__FEATURE_ID", "APP_FEATURE__FEATURE_ID", "inner.join"); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE_FEATURE__APP_ID", "==", appId)); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE_FEATURE__PROFILE_ID", "==", profileId)); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE_FEATURE__ENABLED", "==", Boolean.TRUE)); + for (Map row : QueryExecutionUtility.flushRsToMap(securityDb, qs)) { + String featureKey = (String) row.get("featureKey"); + if (!result.containsKey(featureKey)) { + Map featureInfo = new HashMap<>(); + featureInfo.put("featureId", row.get("featureId")); + featureInfo.put("profileName", profileName); + featureInfo.put("isDefaultProfile", isDefaultProfile); + result.put(featureKey, featureInfo); } - } catch (SQLException e) { - classLogger.error("Failed to add profile features to result", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); } } private static void addSubgroupFeaturesToResult(IRDBMSEngine securityDb, String appId, String subgroupId, String subgroupName, String parentProfileName, Map result) { - String sql = "SELECT f.FEATURE_KEY, f.FEATURE_ID " - + "FROM APP_SUBGROUP_FEATURE sf " - + "JOIN APP_FEATURE f ON sf.FEATURE_ID = f.FEATURE_ID AND sf.APP_ID = f.APP_ID " - + "WHERE sf.APP_ID=? AND sf.SUBGROUP_ID=? AND sf.ENABLED=TRUE"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(sql); - ps.setString(1, appId); - ps.setString(2, subgroupId); - rs = ps.executeQuery(); - while (rs.next()) { - String featureKey = rs.getString("FEATURE_KEY"); - if (!result.containsKey(featureKey)) { - Map featureInfo = new HashMap<>(); - featureInfo.put("featureId", rs.getString("FEATURE_ID")); - featureInfo.put("profileName", parentProfileName); - featureInfo.put("subgroupName", subgroupName); - featureInfo.put("isDefaultProfile", false); - result.put(featureKey, featureInfo); - } + SelectQueryStruct qs = new SelectQueryStruct(); + qs.addSelector(new QueryColumnSelector("APP_FEATURE__FEATURE_KEY", "featureKey")); + qs.addSelector(new QueryColumnSelector("APP_FEATURE__FEATURE_ID", "featureId")); + qs.addRelation("APP_SUBGROUP_FEATURE__FEATURE_ID", "APP_FEATURE__FEATURE_ID", "inner.join"); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_SUBGROUP_FEATURE__APP_ID", "==", appId)); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_SUBGROUP_FEATURE__SUBGROUP_ID", "==", subgroupId)); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_SUBGROUP_FEATURE__ENABLED", "==", Boolean.TRUE)); + for (Map row : QueryExecutionUtility.flushRsToMap(securityDb, qs)) { + String featureKey = (String) row.get("featureKey"); + if (!result.containsKey(featureKey)) { + Map featureInfo = new HashMap<>(); + featureInfo.put("featureId", row.get("featureId")); + featureInfo.put("profileName", parentProfileName); + featureInfo.put("subgroupName", subgroupName); + featureInfo.put("isDefaultProfile", false); + result.put(featureKey, featureInfo); } - } catch (SQLException e) { - classLogger.error("Failed to add subgroup features to result", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); } } private static String resolveFeatureId(IRDBMSEngine securityDb, String appId, String featureKey) { - String sql = "SELECT FEATURE_ID FROM APP_FEATURE WHERE APP_ID=? AND FEATURE_KEY=?"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(sql); - ps.setString(1, appId); - ps.setString(2, featureKey); - rs = ps.executeQuery(); - if (rs.next()) return rs.getString("FEATURE_ID"); - } catch (SQLException e) { - classLogger.error("Failed to resolve feature ID", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); - } - return null; + SelectQueryStruct qs = new SelectQueryStruct(); + qs.addSelector(new QueryColumnSelector("APP_FEATURE__FEATURE_ID", "featureId")); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_FEATURE__APP_ID", "==", appId)); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_FEATURE__FEATURE_KEY", "==", featureKey)); + return QueryExecutionUtility.flushToString(securityDb, qs); } private static boolean queryFeatureEnabled(IRDBMSEngine securityDb, String appId, String profileId, String featureId) { - String sql = "SELECT ENABLED FROM APP_PROFILE_FEATURE WHERE APP_ID=? AND PROFILE_ID=? AND FEATURE_ID=?"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(sql); - ps.setString(1, appId); - ps.setString(2, profileId); - ps.setString(3, featureId); - rs = ps.executeQuery(); - if (rs.next()) return rs.getBoolean("ENABLED"); - } catch (SQLException e) { - classLogger.error("Failed to check feature enabled", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); - } - return false; + SelectQueryStruct qs = new SelectQueryStruct(); + qs.addSelector(new QueryColumnSelector("APP_PROFILE_FEATURE__ENABLED", "enabled")); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE_FEATURE__APP_ID", "==", appId)); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE_FEATURE__PROFILE_ID", "==", profileId)); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE_FEATURE__FEATURE_ID", "==", featureId)); + List> rows = QueryExecutionUtility.flushRsToMap(securityDb, qs); + return !rows.isEmpty() && Boolean.TRUE.equals(rows.get(0).get("enabled")); } private static boolean querySubgroupFeatureEnabled(IRDBMSEngine securityDb, String appId, String subgroupId, String featureId) { - String sql = "SELECT ENABLED FROM APP_SUBGROUP_FEATURE WHERE APP_ID=? AND SUBGROUP_ID=? AND FEATURE_ID=?"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(sql); - ps.setString(1, appId); - ps.setString(2, subgroupId); - ps.setString(3, featureId); - rs = ps.executeQuery(); - if (rs.next()) return rs.getBoolean("ENABLED"); - } catch (SQLException e) { - classLogger.error("Failed to check subgroup feature enabled", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); - } - return false; + SelectQueryStruct qs = new SelectQueryStruct(); + qs.addSelector(new QueryColumnSelector("APP_SUBGROUP_FEATURE__ENABLED", "enabled")); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_SUBGROUP_FEATURE__APP_ID", "==", appId)); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_SUBGROUP_FEATURE__SUBGROUP_ID", "==", subgroupId)); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_SUBGROUP_FEATURE__FEATURE_ID", "==", featureId)); + List> rows = QueryExecutionUtility.flushRsToMap(securityDb, qs); + return !rows.isEmpty() && Boolean.TRUE.equals(rows.get(0).get("enabled")); } private static boolean isGroupProfile(String appId, String profileId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - String sql = "SELECT IS_GROUP FROM APP_PROFILE WHERE APP_ID=? AND PROFILE_ID=?"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(sql); - ps.setString(1, appId); - ps.setString(2, profileId); - rs = ps.executeQuery(); - if (rs.next()) return rs.getBoolean("IS_GROUP"); - } catch (SQLException e) { - classLogger.error("Failed to check isGroup on profile", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); - } - return false; + SelectQueryStruct qs = new SelectQueryStruct(); + qs.addSelector(new QueryColumnSelector("APP_PROFILE__IS_GROUP", "isGroup")); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE__APP_ID", "==", appId)); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE__PROFILE_ID", "==", profileId)); + List> rows = QueryExecutionUtility.flushRsToMap(securityDb, qs); + return !rows.isEmpty() && Boolean.TRUE.equals(rows.get(0).get("isGroup")); } private static List getSubgroupIdsForProfile(IRDBMSEngine securityDb, String appId, String profileId) { + SelectQueryStruct qs = new SelectQueryStruct(); + qs.addSelector(new QueryColumnSelector("APP_PROFILE_SUBGROUP__SUBGROUP_ID", "subgroupId")); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE_SUBGROUP__APP_ID", "==", appId)); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE_SUBGROUP__PROFILE_ID", "==", profileId)); List ids = new ArrayList<>(); - String sql = "SELECT SUBGROUP_ID FROM APP_PROFILE_SUBGROUP WHERE APP_ID=? AND PROFILE_ID=?"; - PreparedStatement ps = null; - ResultSet rs = null; - try { - ps = securityDb.getPreparedStatement(sql); - ps.setString(1, appId); - ps.setString(2, profileId); - rs = ps.executeQuery(); - while (rs.next()) ids.add(rs.getString("SUBGROUP_ID")); - } catch (SQLException e) { - classLogger.error("Failed to get subgroup IDs for profile", e); - } finally { - ConnectionUtils.closeAllConnections(ps, rs); + for (Map r : QueryExecutionUtility.flushRsToMap(securityDb, qs)) { + ids.add((String) r.get("subgroupId")); } return ids; } diff --git a/src/prerna/reactor/appprofile/AssignUserProfileReactor.java b/src/prerna/reactor/appprofile/AssignUserProfileReactor.java deleted file mode 100644 index 15facd02d32..00000000000 --- a/src/prerna/reactor/appprofile/AssignUserProfileReactor.java +++ /dev/null @@ -1,80 +0,0 @@ -/******************************************************************************* - * Copyright 2015 Defense Health Agency (DHA) - * - * If your use of this software does not include any GPLv2 components: - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ---------------------------------------------------------------------------- - * If your use of this software includes any GPLv2 components: - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - *******************************************************************************/ -package prerna.reactor.appprofile; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; -import prerna.reactor.AbstractReactor; -import prerna.sablecc2.om.PixelDataType; -import prerna.sablecc2.om.PixelOperationType; -import prerna.sablecc2.om.ReactorKeysEnum; -import prerna.sablecc2.om.nounmeta.NounMetadata; - -/** - * @deprecated Use {@link AssignAppUserProfileReactor} (Pixel: AssignAppUserProfile). - * Kept for backwards compatibility. - */ -@Deprecated -/** - * Assign a user to a profile for an app. A user can be in multiple profiles simultaneously. - * - *

Pixel: {@code AssignUserProfile(app=["appId"], userId=["userId"], profile=["profileId"]);}

- */ -public class AssignUserProfileReactor extends AbstractReactor { - - private static final Logger classLogger = LogManager.getLogger(AssignUserProfileReactor.class); - - public AssignUserProfileReactor() { - this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.USER_ID.getKey(), ReactorKeysEnum.PROFILE_ID.getKey() }; - this.keyRequired = new int[] { 1, 1, 1 }; - } - - @Override - public NounMetadata execute() { - organizeKeys(); - User user = this.insight.getUser(); - String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); - String userId = this.keyValue.get(ReactorKeysEnum.USER_ID.getKey()); - String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); - - if (!AppProfileUtils.canAssignProfiles(user, appId)) { - throw new IllegalArgumentException("User does not have permission to assign profiles for this app."); - } - AppProfileUtils.assignUserProfile(appId, userId, profileId, user); - NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); - noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("User assigned to profile.")); - return noun; - } - - @Override - public String getReactorDescription() { - return "Assign a user to a profile for an app. A user can be in multiple profiles simultaneously."; - } -} diff --git a/src/prerna/reactor/appprofile/GetUserAppProfileReactor.java b/src/prerna/reactor/appprofile/GetUserAppProfileReactor.java deleted file mode 100644 index c2f254f2422..00000000000 --- a/src/prerna/reactor/appprofile/GetUserAppProfileReactor.java +++ /dev/null @@ -1,77 +0,0 @@ -/******************************************************************************* - * Copyright 2015 Defense Health Agency (DHA) - * - * If your use of this software does not include any GPLv2 components: - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ---------------------------------------------------------------------------- - * If your use of this software includes any GPLv2 components: - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - *******************************************************************************/ -package prerna.reactor.appprofile; - -import java.util.Map; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; -import prerna.reactor.AbstractReactor; -import prerna.sablecc2.om.PixelDataType; -import prerna.sablecc2.om.PixelOperationType; -import prerna.sablecc2.om.ReactorKeysEnum; -import prerna.sablecc2.om.nounmeta.NounMetadata; - -/** - * @deprecated Use {@link GetUserAppProfilesReactor} (Pixel: GetUserAppProfiles). Identical behavior. - */ -@Deprecated -/** - * Alias for GetUserAppProfiles — identical behavior. Get the calling user's profile and subgroup memberships for an app. - * - *

Pixel: {@code GetUserAppProfile(app=["appId"]);}

- */ -public class GetUserAppProfileReactor extends AbstractReactor { - - private static final Logger classLogger = LogManager.getLogger(GetUserAppProfileReactor.class); - - public GetUserAppProfileReactor() { - this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey() }; - this.keyRequired = new int[] { 1 }; - } - - @Override - public NounMetadata execute() { - organizeKeys(); - User user = this.insight.getUser(); - String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); - - if (!AppProfileUtils.canEvaluateFeatures(user, appId)) { - throw new IllegalArgumentException("User does not have access to this app."); - } - Map result = AppProfileUtils.getUserAppProfiles(appId, user); - return new NounMetadata(result, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); - } - - @Override - public String getReactorDescription() { - return "Alias for GetUserAppProfiles — identical behavior. Get the calling user's profile and subgroup memberships for an app."; - } -} diff --git a/src/prerna/reactor/appprofile/GetUserFeaturesReactor.java b/src/prerna/reactor/appprofile/GetUserFeaturesReactor.java deleted file mode 100644 index a74c488bade..00000000000 --- a/src/prerna/reactor/appprofile/GetUserFeaturesReactor.java +++ /dev/null @@ -1,78 +0,0 @@ -/******************************************************************************* - * Copyright 2015 Defense Health Agency (DHA) - * - * If your use of this software does not include any GPLv2 components: - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ---------------------------------------------------------------------------- - * If your use of this software includes any GPLv2 components: - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - *******************************************************************************/ -package prerna.reactor.appprofile; - -import java.util.Map; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; -import prerna.reactor.AbstractReactor; -import prerna.sablecc2.om.PixelDataType; -import prerna.sablecc2.om.PixelOperationType; -import prerna.sablecc2.om.ReactorKeysEnum; -import prerna.sablecc2.om.nounmeta.NounMetadata; - -/** - * @deprecated Use {@link GetAppUserFeaturesReactor} (Pixel: GetAppUserFeatures). - */ -@Deprecated -/** - * Get all enabled features for the calling user in an app. - * - *

Pixel: {@code GetUserFeatures(app=["appId"]);}

- */ -public class GetUserFeaturesReactor extends AbstractReactor { - - private static final Logger classLogger = LogManager.getLogger(GetUserFeaturesReactor.class); - - public GetUserFeaturesReactor() { - this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey() }; - this.keyRequired = new int[] { 1 }; - } - - @Override - public NounMetadata execute() { - organizeKeys(); - User user = this.insight.getUser(); - String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); - - if (!AppProfileUtils.canEvaluateFeatures(user, appId)) { - throw new IllegalArgumentException("User does not have access to this app."); - } - // Returns only enabled features — callers cannot infer what features exist but are hidden - Map features = AppProfileUtils.getUserFeatures(appId, user); - return new NounMetadata(features, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); - } - - @Override - public String getReactorDescription() { - return "Get all enabled features for the calling user in an app."; - } -} diff --git a/src/prerna/reactor/appprofile/RemoveUserProfileReactor.java b/src/prerna/reactor/appprofile/RemoveUserProfileReactor.java deleted file mode 100644 index 3b967d76095..00000000000 --- a/src/prerna/reactor/appprofile/RemoveUserProfileReactor.java +++ /dev/null @@ -1,81 +0,0 @@ -/******************************************************************************* - * Copyright 2015 Defense Health Agency (DHA) - * - * If your use of this software does not include any GPLv2 components: - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ---------------------------------------------------------------------------- - * If your use of this software includes any GPLv2 components: - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - *******************************************************************************/ -package prerna.reactor.appprofile; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; -import prerna.reactor.AbstractReactor; -import prerna.sablecc2.om.PixelDataType; -import prerna.sablecc2.om.PixelOperationType; -import prerna.sablecc2.om.ReactorKeysEnum; -import prerna.sablecc2.om.nounmeta.NounMetadata; - -/** - * @deprecated Use {@link RemoveAppUserProfileReactor} (Pixel: RemoveAppUserProfile) to remove a user from a specific profile. - * WARNING: This reactor removes the user from ALL profiles for the app, not just one. - * The new RemoveAppUserProfile reactor requires a profileId and removes only that assignment. - * Kept for backwards compatibility. - */ -@Deprecated -/** - * Remove a user from all profile assignments for an app. - * - *

Pixel: {@code RemoveUserProfile(app=["appId"], userId=["userId"]);}

- */ -public class RemoveUserProfileReactor extends AbstractReactor { - - private static final Logger classLogger = LogManager.getLogger(RemoveUserProfileReactor.class); - - public RemoveUserProfileReactor() { - this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.USER_ID.getKey() }; - this.keyRequired = new int[] { 1, 1 }; - } - - @Override - public NounMetadata execute() { - organizeKeys(); - User user = this.insight.getUser(); - String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); - String userId = this.keyValue.get(ReactorKeysEnum.USER_ID.getKey()); - - if (!AppProfileUtils.canAssignProfiles(user, appId)) { - throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); - } - AppProfileUtils.removeUserProfile(appId, userId); - NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); - noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("User removed from all profiles.")); - return noun; - } - - @Override - public String getReactorDescription() { - return "Remove a user from all profile assignments for an app."; - } -} diff --git a/src/prerna/reactor/appprofile/CheckAppFeatureReactor.java b/src/prerna/reactor/appprofile/feature/CheckAppFeatureReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/CheckAppFeatureReactor.java rename to src/prerna/reactor/appprofile/feature/CheckAppFeatureReactor.java index 44ecda29ea6..7f0d0b42aad 100644 --- a/src/prerna/reactor/appprofile/CheckAppFeatureReactor.java +++ b/src/prerna/reactor/appprofile/feature/CheckAppFeatureReactor.java @@ -25,12 +25,13 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.feature; + +import prerna.reactor.appprofile.AppProfileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; diff --git a/src/prerna/reactor/appprofile/CheckFeatureReactor.java b/src/prerna/reactor/appprofile/feature/CheckFeatureReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/CheckFeatureReactor.java rename to src/prerna/reactor/appprofile/feature/CheckFeatureReactor.java index e767b3921e7..8dbac444b96 100644 --- a/src/prerna/reactor/appprofile/CheckFeatureReactor.java +++ b/src/prerna/reactor/appprofile/feature/CheckFeatureReactor.java @@ -25,12 +25,13 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.feature; + +import prerna.reactor.appprofile.AppProfileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; diff --git a/src/prerna/reactor/appprofile/CreateAppFeatureReactor.java b/src/prerna/reactor/appprofile/feature/CreateAppFeatureReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/CreateAppFeatureReactor.java rename to src/prerna/reactor/appprofile/feature/CreateAppFeatureReactor.java index 9c3c97b1271..bb12200be55 100644 --- a/src/prerna/reactor/appprofile/CreateAppFeatureReactor.java +++ b/src/prerna/reactor/appprofile/feature/CreateAppFeatureReactor.java @@ -25,14 +25,15 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.feature; + +import prerna.reactor.appprofile.AppProfileUtils; import java.util.Map; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; diff --git a/src/prerna/reactor/appprofile/DeleteAppFeatureReactor.java b/src/prerna/reactor/appprofile/feature/DeleteAppFeatureReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/DeleteAppFeatureReactor.java rename to src/prerna/reactor/appprofile/feature/DeleteAppFeatureReactor.java index c12f0a40eb4..87af09126a6 100644 --- a/src/prerna/reactor/appprofile/DeleteAppFeatureReactor.java +++ b/src/prerna/reactor/appprofile/feature/DeleteAppFeatureReactor.java @@ -25,12 +25,13 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.feature; + +import prerna.reactor.appprofile.AppProfileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; diff --git a/src/prerna/reactor/appprofile/GetAppFeaturesReactor.java b/src/prerna/reactor/appprofile/feature/GetAppFeaturesReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/GetAppFeaturesReactor.java rename to src/prerna/reactor/appprofile/feature/GetAppFeaturesReactor.java index 48bee4c1c6a..93831f0b8bf 100644 --- a/src/prerna/reactor/appprofile/GetAppFeaturesReactor.java +++ b/src/prerna/reactor/appprofile/feature/GetAppFeaturesReactor.java @@ -25,7 +25,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.feature; + +import prerna.reactor.appprofile.AppProfileUtils; import java.util.List; import java.util.Map; @@ -33,7 +35,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; diff --git a/src/prerna/reactor/appprofile/GetAppUserFeaturesReactor.java b/src/prerna/reactor/appprofile/feature/GetAppUserFeaturesReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/GetAppUserFeaturesReactor.java rename to src/prerna/reactor/appprofile/feature/GetAppUserFeaturesReactor.java index e51ce0d64a7..4db7a697619 100644 --- a/src/prerna/reactor/appprofile/GetAppUserFeaturesReactor.java +++ b/src/prerna/reactor/appprofile/feature/GetAppUserFeaturesReactor.java @@ -25,14 +25,15 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.feature; + +import prerna.reactor.appprofile.AppProfileUtils; import java.util.Map; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; diff --git a/src/prerna/reactor/appprofile/UpdateAppFeatureReactor.java b/src/prerna/reactor/appprofile/feature/UpdateAppFeatureReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/UpdateAppFeatureReactor.java rename to src/prerna/reactor/appprofile/feature/UpdateAppFeatureReactor.java index e43f544c47a..efbf41c1b2b 100644 --- a/src/prerna/reactor/appprofile/UpdateAppFeatureReactor.java +++ b/src/prerna/reactor/appprofile/feature/UpdateAppFeatureReactor.java @@ -25,12 +25,13 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.feature; + +import prerna.reactor.appprofile.AppProfileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; diff --git a/src/prerna/reactor/appprofile/AddAppProfileManagerReactor.java b/src/prerna/reactor/appprofile/manager/AddAppProfileManagerReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/AddAppProfileManagerReactor.java rename to src/prerna/reactor/appprofile/manager/AddAppProfileManagerReactor.java index 8d4bafba4af..1231611b29b 100644 --- a/src/prerna/reactor/appprofile/AddAppProfileManagerReactor.java +++ b/src/prerna/reactor/appprofile/manager/AddAppProfileManagerReactor.java @@ -25,7 +25,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.manager; + +import prerna.reactor.appprofile.AppProfileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -33,7 +35,6 @@ import prerna.sablecc2.om.ReactorKeysEnum; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.nounmeta.NounMetadata; diff --git a/src/prerna/reactor/appprofile/GetAppProfileManagersReactor.java b/src/prerna/reactor/appprofile/manager/GetAppProfileManagersReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/GetAppProfileManagersReactor.java rename to src/prerna/reactor/appprofile/manager/GetAppProfileManagersReactor.java index 2cfb289281a..31fd203df2f 100644 --- a/src/prerna/reactor/appprofile/GetAppProfileManagersReactor.java +++ b/src/prerna/reactor/appprofile/manager/GetAppProfileManagersReactor.java @@ -25,7 +25,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.manager; + +import prerna.reactor.appprofile.AppProfileUtils; import java.util.List; import java.util.Map; @@ -36,7 +38,6 @@ import prerna.sablecc2.om.ReactorKeysEnum; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.nounmeta.NounMetadata; diff --git a/src/prerna/reactor/appprofile/RemoveAppProfileManagerReactor.java b/src/prerna/reactor/appprofile/manager/RemoveAppProfileManagerReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/RemoveAppProfileManagerReactor.java rename to src/prerna/reactor/appprofile/manager/RemoveAppProfileManagerReactor.java index 56e726a751f..f59a835036d 100644 --- a/src/prerna/reactor/appprofile/RemoveAppProfileManagerReactor.java +++ b/src/prerna/reactor/appprofile/manager/RemoveAppProfileManagerReactor.java @@ -25,7 +25,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.manager; + +import prerna.reactor.appprofile.AppProfileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -33,7 +35,6 @@ import prerna.sablecc2.om.ReactorKeysEnum; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.nounmeta.NounMetadata; diff --git a/src/prerna/reactor/appprofile/CreateAppProfileReactor.java b/src/prerna/reactor/appprofile/profile/CreateAppProfileReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/CreateAppProfileReactor.java rename to src/prerna/reactor/appprofile/profile/CreateAppProfileReactor.java index ed0ff7f0c82..60f81dbc782 100644 --- a/src/prerna/reactor/appprofile/CreateAppProfileReactor.java +++ b/src/prerna/reactor/appprofile/profile/CreateAppProfileReactor.java @@ -25,14 +25,15 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.profile; + +import prerna.reactor.appprofile.AppProfileUtils; import java.util.Map; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; diff --git a/src/prerna/reactor/appprofile/DeleteAppProfileReactor.java b/src/prerna/reactor/appprofile/profile/DeleteAppProfileReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/DeleteAppProfileReactor.java rename to src/prerna/reactor/appprofile/profile/DeleteAppProfileReactor.java index 17a31431b76..9f290dd5d75 100644 --- a/src/prerna/reactor/appprofile/DeleteAppProfileReactor.java +++ b/src/prerna/reactor/appprofile/profile/DeleteAppProfileReactor.java @@ -25,12 +25,13 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.profile; + +import prerna.reactor.appprofile.AppProfileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; diff --git a/src/prerna/reactor/appprofile/GetAppProfileFeaturesReactor.java b/src/prerna/reactor/appprofile/profile/GetAppProfileFeaturesReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/GetAppProfileFeaturesReactor.java rename to src/prerna/reactor/appprofile/profile/GetAppProfileFeaturesReactor.java index 095f7c76bae..c1c10a78ba3 100644 --- a/src/prerna/reactor/appprofile/GetAppProfileFeaturesReactor.java +++ b/src/prerna/reactor/appprofile/profile/GetAppProfileFeaturesReactor.java @@ -25,7 +25,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.profile; + +import prerna.reactor.appprofile.AppProfileUtils; import java.util.List; import java.util.Map; @@ -36,7 +38,6 @@ import prerna.sablecc2.om.ReactorKeysEnum; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.nounmeta.NounMetadata; diff --git a/src/prerna/reactor/appprofile/GetAppProfileUsersReactor.java b/src/prerna/reactor/appprofile/profile/GetAppProfileUsersReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/GetAppProfileUsersReactor.java rename to src/prerna/reactor/appprofile/profile/GetAppProfileUsersReactor.java index 005545fa722..62f1b230a6e 100644 --- a/src/prerna/reactor/appprofile/GetAppProfileUsersReactor.java +++ b/src/prerna/reactor/appprofile/profile/GetAppProfileUsersReactor.java @@ -25,7 +25,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.profile; + +import prerna.reactor.appprofile.AppProfileUtils; import java.util.List; import java.util.Map; @@ -36,7 +38,6 @@ import prerna.sablecc2.om.ReactorKeysEnum; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.nounmeta.NounMetadata; diff --git a/src/prerna/reactor/appprofile/GetAppProfilesReactor.java b/src/prerna/reactor/appprofile/profile/GetAppProfilesReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/GetAppProfilesReactor.java rename to src/prerna/reactor/appprofile/profile/GetAppProfilesReactor.java index 779c469fcd1..430c09b57f0 100644 --- a/src/prerna/reactor/appprofile/GetAppProfilesReactor.java +++ b/src/prerna/reactor/appprofile/profile/GetAppProfilesReactor.java @@ -25,7 +25,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.profile; + +import prerna.reactor.appprofile.AppProfileUtils; import java.util.List; import java.util.Map; @@ -33,7 +35,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; diff --git a/src/prerna/reactor/appprofile/SetAppProfileFeatureReactor.java b/src/prerna/reactor/appprofile/profile/SetAppProfileFeatureReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/SetAppProfileFeatureReactor.java rename to src/prerna/reactor/appprofile/profile/SetAppProfileFeatureReactor.java index 60c619db784..39ab41e6b70 100644 --- a/src/prerna/reactor/appprofile/SetAppProfileFeatureReactor.java +++ b/src/prerna/reactor/appprofile/profile/SetAppProfileFeatureReactor.java @@ -25,7 +25,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.profile; + +import prerna.reactor.appprofile.AppProfileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -33,7 +35,6 @@ import prerna.sablecc2.om.ReactorKeysEnum; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.nounmeta.NounMetadata; diff --git a/src/prerna/reactor/appprofile/UpdateAppProfileReactor.java b/src/prerna/reactor/appprofile/profile/UpdateAppProfileReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/UpdateAppProfileReactor.java rename to src/prerna/reactor/appprofile/profile/UpdateAppProfileReactor.java index 84bd762271c..7894b9cf98f 100644 --- a/src/prerna/reactor/appprofile/UpdateAppProfileReactor.java +++ b/src/prerna/reactor/appprofile/profile/UpdateAppProfileReactor.java @@ -25,12 +25,13 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.profile; + +import prerna.reactor.appprofile.AppProfileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; diff --git a/src/prerna/reactor/appprofile/AssignAppUserSubgroupReactor.java b/src/prerna/reactor/appprofile/subgroup/AssignAppUserSubgroupReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/AssignAppUserSubgroupReactor.java rename to src/prerna/reactor/appprofile/subgroup/AssignAppUserSubgroupReactor.java index eed892d00b1..9a93fcaf48f 100644 --- a/src/prerna/reactor/appprofile/AssignAppUserSubgroupReactor.java +++ b/src/prerna/reactor/appprofile/subgroup/AssignAppUserSubgroupReactor.java @@ -25,12 +25,13 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.subgroup; + +import prerna.reactor.appprofile.AppProfileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; diff --git a/src/prerna/reactor/appprofile/CreateAppSubgroupReactor.java b/src/prerna/reactor/appprofile/subgroup/CreateAppSubgroupReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/CreateAppSubgroupReactor.java rename to src/prerna/reactor/appprofile/subgroup/CreateAppSubgroupReactor.java index 5b49795c7e7..3f7f71c2383 100644 --- a/src/prerna/reactor/appprofile/CreateAppSubgroupReactor.java +++ b/src/prerna/reactor/appprofile/subgroup/CreateAppSubgroupReactor.java @@ -25,14 +25,15 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.subgroup; + +import prerna.reactor.appprofile.AppProfileUtils; import java.util.Map; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; diff --git a/src/prerna/reactor/appprofile/DeleteAppSubgroupReactor.java b/src/prerna/reactor/appprofile/subgroup/DeleteAppSubgroupReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/DeleteAppSubgroupReactor.java rename to src/prerna/reactor/appprofile/subgroup/DeleteAppSubgroupReactor.java index e2981d064f2..078ac3dc444 100644 --- a/src/prerna/reactor/appprofile/DeleteAppSubgroupReactor.java +++ b/src/prerna/reactor/appprofile/subgroup/DeleteAppSubgroupReactor.java @@ -25,12 +25,13 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.subgroup; + +import prerna.reactor.appprofile.AppProfileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; diff --git a/src/prerna/reactor/appprofile/GetAppSubgroupFeaturesReactor.java b/src/prerna/reactor/appprofile/subgroup/GetAppSubgroupFeaturesReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/GetAppSubgroupFeaturesReactor.java rename to src/prerna/reactor/appprofile/subgroup/GetAppSubgroupFeaturesReactor.java index 2aa24975ffc..3982e5aeaff 100644 --- a/src/prerna/reactor/appprofile/GetAppSubgroupFeaturesReactor.java +++ b/src/prerna/reactor/appprofile/subgroup/GetAppSubgroupFeaturesReactor.java @@ -25,7 +25,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.subgroup; + +import prerna.reactor.appprofile.AppProfileUtils; import java.util.List; import java.util.Map; @@ -36,7 +38,6 @@ import prerna.sablecc2.om.ReactorKeysEnum; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.nounmeta.NounMetadata; diff --git a/src/prerna/reactor/appprofile/GetAppSubgroupUsersReactor.java b/src/prerna/reactor/appprofile/subgroup/GetAppSubgroupUsersReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/GetAppSubgroupUsersReactor.java rename to src/prerna/reactor/appprofile/subgroup/GetAppSubgroupUsersReactor.java index 58d7376d6fa..7cd816fad27 100644 --- a/src/prerna/reactor/appprofile/GetAppSubgroupUsersReactor.java +++ b/src/prerna/reactor/appprofile/subgroup/GetAppSubgroupUsersReactor.java @@ -25,7 +25,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.subgroup; + +import prerna.reactor.appprofile.AppProfileUtils; import java.util.List; import java.util.Map; @@ -36,7 +38,6 @@ import prerna.sablecc2.om.ReactorKeysEnum; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.nounmeta.NounMetadata; diff --git a/src/prerna/reactor/appprofile/GetAppSubgroupsReactor.java b/src/prerna/reactor/appprofile/subgroup/GetAppSubgroupsReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/GetAppSubgroupsReactor.java rename to src/prerna/reactor/appprofile/subgroup/GetAppSubgroupsReactor.java index 66205cc9efc..1829595d266 100644 --- a/src/prerna/reactor/appprofile/GetAppSubgroupsReactor.java +++ b/src/prerna/reactor/appprofile/subgroup/GetAppSubgroupsReactor.java @@ -25,7 +25,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.subgroup; + +import prerna.reactor.appprofile.AppProfileUtils; import java.util.List; import java.util.Map; @@ -33,7 +35,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; diff --git a/src/prerna/reactor/appprofile/RemoveAppUserSubgroupReactor.java b/src/prerna/reactor/appprofile/subgroup/RemoveAppUserSubgroupReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/RemoveAppUserSubgroupReactor.java rename to src/prerna/reactor/appprofile/subgroup/RemoveAppUserSubgroupReactor.java index 33f4f4a4495..240e0b17382 100644 --- a/src/prerna/reactor/appprofile/RemoveAppUserSubgroupReactor.java +++ b/src/prerna/reactor/appprofile/subgroup/RemoveAppUserSubgroupReactor.java @@ -25,12 +25,13 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.subgroup; + +import prerna.reactor.appprofile.AppProfileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; diff --git a/src/prerna/reactor/appprofile/SetAppSubgroupFeatureReactor.java b/src/prerna/reactor/appprofile/subgroup/SetAppSubgroupFeatureReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/SetAppSubgroupFeatureReactor.java rename to src/prerna/reactor/appprofile/subgroup/SetAppSubgroupFeatureReactor.java index d2a87c024ef..a37b774b1cb 100644 --- a/src/prerna/reactor/appprofile/SetAppSubgroupFeatureReactor.java +++ b/src/prerna/reactor/appprofile/subgroup/SetAppSubgroupFeatureReactor.java @@ -25,7 +25,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.subgroup; + +import prerna.reactor.appprofile.AppProfileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -33,7 +35,6 @@ import prerna.sablecc2.om.ReactorKeysEnum; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.nounmeta.NounMetadata; diff --git a/src/prerna/reactor/appprofile/UpdateAppSubgroupReactor.java b/src/prerna/reactor/appprofile/subgroup/UpdateAppSubgroupReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/UpdateAppSubgroupReactor.java rename to src/prerna/reactor/appprofile/subgroup/UpdateAppSubgroupReactor.java index a5deeec9c22..4d1359d5dd2 100644 --- a/src/prerna/reactor/appprofile/UpdateAppSubgroupReactor.java +++ b/src/prerna/reactor/appprofile/subgroup/UpdateAppSubgroupReactor.java @@ -25,12 +25,13 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.subgroup; + +import prerna.reactor.appprofile.AppProfileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; diff --git a/src/prerna/reactor/appprofile/AssignAppUserProfileReactor.java b/src/prerna/reactor/appprofile/user/AssignAppUserProfileReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/AssignAppUserProfileReactor.java rename to src/prerna/reactor/appprofile/user/AssignAppUserProfileReactor.java index abb60b832a0..a2ed85c7def 100644 --- a/src/prerna/reactor/appprofile/AssignAppUserProfileReactor.java +++ b/src/prerna/reactor/appprofile/user/AssignAppUserProfileReactor.java @@ -25,12 +25,13 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.user; + +import prerna.reactor.appprofile.AppProfileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; diff --git a/src/prerna/reactor/appprofile/GetUserAppProfilesReactor.java b/src/prerna/reactor/appprofile/user/GetUserAppProfilesReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/GetUserAppProfilesReactor.java rename to src/prerna/reactor/appprofile/user/GetUserAppProfilesReactor.java index fcb2dab9b25..cf9362a4eb0 100644 --- a/src/prerna/reactor/appprofile/GetUserAppProfilesReactor.java +++ b/src/prerna/reactor/appprofile/user/GetUserAppProfilesReactor.java @@ -25,14 +25,15 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.user; + +import prerna.reactor.appprofile.AppProfileUtils; import java.util.Map; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; diff --git a/src/prerna/reactor/appprofile/GetUserProfileReactor.java b/src/prerna/reactor/appprofile/user/GetUserProfileReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/GetUserProfileReactor.java rename to src/prerna/reactor/appprofile/user/GetUserProfileReactor.java index 57e32fe881a..832a5dc9468 100644 --- a/src/prerna/reactor/appprofile/GetUserProfileReactor.java +++ b/src/prerna/reactor/appprofile/user/GetUserProfileReactor.java @@ -25,7 +25,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.user; + +import prerna.reactor.appprofile.AppProfileUtils; import java.util.List; import java.util.Map; @@ -33,7 +35,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; diff --git a/src/prerna/reactor/appprofile/RemoveAppUserProfileReactor.java b/src/prerna/reactor/appprofile/user/RemoveAppUserProfileReactor.java similarity index 98% rename from src/prerna/reactor/appprofile/RemoveAppUserProfileReactor.java rename to src/prerna/reactor/appprofile/user/RemoveAppUserProfileReactor.java index 895ba9e6019..ff68e79e0b8 100644 --- a/src/prerna/reactor/appprofile/RemoveAppUserProfileReactor.java +++ b/src/prerna/reactor/appprofile/user/RemoveAppUserProfileReactor.java @@ -25,12 +25,13 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *******************************************************************************/ -package prerna.reactor.appprofile; +package prerna.reactor.appprofile.user; + +import prerna.reactor.appprofile.AppProfileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import prerna.auth.User; -import prerna.reactor.appprofile.AppProfileUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; diff --git a/src/prerna/reactor/platformprofile/AssignUserPlatformProfileReactor.java b/src/prerna/reactor/platformprofile/AssignUserPlatformProfileReactor.java index 881b67e59e7..4b7a50646ed 100644 --- a/src/prerna/reactor/platformprofile/AssignUserPlatformProfileReactor.java +++ b/src/prerna/reactor/platformprofile/AssignUserPlatformProfileReactor.java @@ -28,6 +28,7 @@ package prerna.reactor.platformprofile; import prerna.auth.User; +import prerna.auth.utils.SecurityAdminUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; @@ -50,7 +51,7 @@ public AssignUserPlatformProfileReactor() { public NounMetadata execute() { organizeKeys(); User user = this.insight.getUser(); - if (!PlatformProfileUtils.canManage(user)) { + if (!SecurityAdminUtils.userIsAdmin(user)) { throw new IllegalArgumentException("User must be an admin to manage platform profiles."); } String userId = this.keyValue.get(ReactorKeysEnum.USER_ID.getKey()); diff --git a/src/prerna/reactor/platformprofile/CreatePlatformProfileReactor.java b/src/prerna/reactor/platformprofile/CreatePlatformProfileReactor.java index 9eea95476c9..d4dcc5b21d3 100644 --- a/src/prerna/reactor/platformprofile/CreatePlatformProfileReactor.java +++ b/src/prerna/reactor/platformprofile/CreatePlatformProfileReactor.java @@ -30,6 +30,7 @@ import java.util.Map; import prerna.auth.User; +import prerna.auth.utils.SecurityAdminUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; @@ -52,7 +53,7 @@ public CreatePlatformProfileReactor() { public NounMetadata execute() { organizeKeys(); User user = this.insight.getUser(); - if (!PlatformProfileUtils.canManage(user)) { + if (!SecurityAdminUtils.userIsAdmin(user)) { throw new IllegalArgumentException("User must be an admin to manage platform profiles."); } String name = this.keyValue.get(ReactorKeysEnum.NAME.getKey()); diff --git a/src/prerna/reactor/platformprofile/DeletePlatformProfileReactor.java b/src/prerna/reactor/platformprofile/DeletePlatformProfileReactor.java index 0b041569934..cadd2ab2149 100644 --- a/src/prerna/reactor/platformprofile/DeletePlatformProfileReactor.java +++ b/src/prerna/reactor/platformprofile/DeletePlatformProfileReactor.java @@ -28,6 +28,7 @@ package prerna.reactor.platformprofile; import prerna.auth.User; +import prerna.auth.utils.SecurityAdminUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; @@ -50,7 +51,7 @@ public DeletePlatformProfileReactor() { public NounMetadata execute() { organizeKeys(); User user = this.insight.getUser(); - if (!PlatformProfileUtils.canManage(user)) { + if (!SecurityAdminUtils.userIsAdmin(user)) { throw new IllegalArgumentException("User must be an admin to manage platform profiles."); } String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); diff --git a/src/prerna/reactor/platformprofile/GetPlatformFeaturesReactor.java b/src/prerna/reactor/platformprofile/GetPlatformFeaturesReactor.java index 449f5851f5f..cae2c7d1559 100644 --- a/src/prerna/reactor/platformprofile/GetPlatformFeaturesReactor.java +++ b/src/prerna/reactor/platformprofile/GetPlatformFeaturesReactor.java @@ -30,6 +30,7 @@ import java.util.Map; import prerna.auth.User; +import prerna.auth.utils.SecurityAdminUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; @@ -52,7 +53,7 @@ public GetPlatformFeaturesReactor() { public NounMetadata execute() { organizeKeys(); User user = this.insight.getUser(); - if (!PlatformProfileUtils.canManage(user)) { + if (!SecurityAdminUtils.userIsAdmin(user)) { throw new IllegalArgumentException("User must be an admin to manage platform profiles."); } String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); diff --git a/src/prerna/reactor/platformprofile/GetPlatformProfileUsersReactor.java b/src/prerna/reactor/platformprofile/GetPlatformProfileUsersReactor.java index 19dd332472e..75a3d8d81aa 100644 --- a/src/prerna/reactor/platformprofile/GetPlatformProfileUsersReactor.java +++ b/src/prerna/reactor/platformprofile/GetPlatformProfileUsersReactor.java @@ -31,6 +31,7 @@ import java.util.Map; import prerna.auth.User; +import prerna.auth.utils.SecurityAdminUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; @@ -53,7 +54,7 @@ public GetPlatformProfileUsersReactor() { public NounMetadata execute() { organizeKeys(); User user = this.insight.getUser(); - if (!PlatformProfileUtils.canManage(user)) { + if (!SecurityAdminUtils.userIsAdmin(user)) { throw new IllegalArgumentException("User must be an admin to manage platform profiles."); } String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); diff --git a/src/prerna/reactor/platformprofile/GetPlatformProfilesReactor.java b/src/prerna/reactor/platformprofile/GetPlatformProfilesReactor.java index 65d2a26595b..2d5f369ce8b 100644 --- a/src/prerna/reactor/platformprofile/GetPlatformProfilesReactor.java +++ b/src/prerna/reactor/platformprofile/GetPlatformProfilesReactor.java @@ -31,6 +31,7 @@ import java.util.Map; import prerna.auth.User; +import prerna.auth.utils.SecurityAdminUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; @@ -52,7 +53,7 @@ public GetPlatformProfilesReactor() { public NounMetadata execute() { organizeKeys(); User user = this.insight.getUser(); - if (!PlatformProfileUtils.canManage(user)) { + if (!SecurityAdminUtils.userIsAdmin(user)) { throw new IllegalArgumentException("User must be an admin to manage platform profiles."); } List> profiles = PlatformProfileUtils.getProfiles(user); diff --git a/src/prerna/reactor/platformprofile/PlatformProfileUtils.java b/src/prerna/reactor/platformprofile/PlatformProfileUtils.java index 00cfca043e8..4236e12884f 100644 --- a/src/prerna/reactor/platformprofile/PlatformProfileUtils.java +++ b/src/prerna/reactor/platformprofile/PlatformProfileUtils.java @@ -43,17 +43,17 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import prerna.auth.AccessToken; import prerna.auth.User; import prerna.engine.api.IRDBMSEngine; -import prerna.engine.api.IRawSelectWrapper; import prerna.query.querystruct.SelectQueryStruct; import prerna.query.querystruct.filters.SimpleQueryFilter; import prerna.query.querystruct.selectors.QueryColumnOrderBySelector; import prerna.query.querystruct.selectors.QueryColumnSelector; import prerna.query.querystruct.selectors.QueryFunctionHelper; import prerna.query.querystruct.selectors.QueryFunctionSelector; -import prerna.rdf.engine.wrappers.WrapperManager; import prerna.util.ConnectionUtils; +import prerna.util.QueryExecutionUtility; import prerna.util.SystemEngineRegistry; import prerna.util.Utility; @@ -71,13 +71,6 @@ public class PlatformProfileUtils { private PlatformProfileUtils() { } - // ─── Permission check ──────────────────────────────────────────────────── - - /** Returns {@code true} if the given user has admin privileges to manage platform profiles. */ - public static boolean canManage(User user) { - return prerna.auth.utils.SecurityAdminUtils.userIsAdmin(user); - } - // ─── Profile CRUD ──────────────────────────────────────────────────────── /** Creates a new platform profile with the given name and description and returns its id, name, and description. */ @@ -86,7 +79,7 @@ public static Map createProfile(String name, String description, throw new IllegalArgumentException("Profile name cannot be blank."); } String profileId = UUID.randomUUID().toString(); - String actorId = prerna.reactor.appprofile.AppProfileUtils.getUserId(user); + String actorId = getUserId(user); IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); PreparedStatement ps = null; try { @@ -179,33 +172,23 @@ public static void deleteProfile(String profileId, User user) { /** Returns all platform profiles ordered by name, each with id, name, description, createdBy, createdAt, and userCount. */ public static List> getProfiles(User user) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - List> profiles = new ArrayList<>(); SelectQueryStruct qs = new SelectQueryStruct(); - qs.addSelector(new QueryColumnSelector("PLATFORM_PROFILE__PROFILE_ID")); - qs.addSelector(new QueryColumnSelector("PLATFORM_PROFILE__PROFILE_NAME")); - qs.addSelector(new QueryColumnSelector("PLATFORM_PROFILE__DESCRIPTION")); - qs.addSelector(new QueryColumnSelector("PLATFORM_PROFILE__CREATED_BY")); - qs.addSelector(new QueryColumnSelector("PLATFORM_PROFILE__CREATED_AT")); + qs.addSelector(new QueryColumnSelector("PLATFORM_PROFILE__PROFILE_ID", "profileId")); + qs.addSelector(new QueryColumnSelector("PLATFORM_PROFILE__PROFILE_NAME", "profileName")); + qs.addSelector(new QueryColumnSelector("PLATFORM_PROFILE__DESCRIPTION", "description")); + qs.addSelector(new QueryColumnSelector("PLATFORM_PROFILE__CREATED_BY", "createdBy")); + qs.addSelector(new QueryColumnSelector("PLATFORM_PROFILE__CREATED_AT", "createdAt")); + qs.addSelector(QueryFunctionSelector.makeFunctionSelector(QueryFunctionHelper.COUNT, "PLATFORM_USER_PROFILE__USER_ID", "userCount")); + qs.addRelation("PLATFORM_PROFILE__PROFILE_ID", "PLATFORM_USER_PROFILE__PROFILE_ID", "left.join"); + qs.addGroupBy(new QueryColumnSelector("PLATFORM_PROFILE__PROFILE_ID")); + qs.addGroupBy(new QueryColumnSelector("PLATFORM_PROFILE__PROFILE_NAME")); + qs.addGroupBy(new QueryColumnSelector("PLATFORM_PROFILE__DESCRIPTION")); + qs.addGroupBy(new QueryColumnSelector("PLATFORM_PROFILE__CREATED_BY")); + qs.addGroupBy(new QueryColumnSelector("PLATFORM_PROFILE__CREATED_AT")); qs.addOrderBy(new QueryColumnOrderBySelector("PLATFORM_PROFILE__PROFILE_NAME")); - try (IRawSelectWrapper wrapper = WrapperManager.getInstance().getRawWrapper(securityDb, qs)) { - while (wrapper.hasNext()) { - Object[] row = wrapper.next().getValues(); - String pid = (String) row[0]; - Map profile = new HashMap<>(); - profile.put("profileId", pid); - profile.put("profileName", row[1]); - profile.put("description", row[2]); - profile.put("createdBy", row[3]); - profile.put("createdAt", row[4]); - profile.put("userCount", getAssignedUserCount(securityDb, pid)); - profiles.add(profile); - } - } catch (Exception e) { - classLogger.error("Failed to get platform profiles", e); - } - return profiles; + return QueryExecutionUtility.flushRsToMap(securityDb, qs); } // ─── Profile-Feature Assignment ────────────────────────────────────────── @@ -261,19 +244,14 @@ public static Map getProfileFeatures(String profileId) { qs.addSelector(new QueryColumnSelector("PLATFORM_PROFILE_FEATURE__ENABLED")); qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("PLATFORM_PROFILE_FEATURE__PROFILE_ID", "==", profileId)); - try (IRawSelectWrapper wrapper = WrapperManager.getInstance().getRawWrapper(securityDb, qs)) { - while (wrapper.hasNext()) { - Object[] row = wrapper.next().getValues(); - String key = (String) row[0]; - if (key != null && PREDEFINED_FEATURE_KEYS.contains(key)) { - Object enabledVal = row[1]; - boolean enabled = enabledVal instanceof Boolean ? (Boolean) enabledVal - : (enabledVal != null && "true".equalsIgnoreCase(enabledVal.toString())); - result.put(key, enabled); - } + for (Object[] row : QueryExecutionUtility.flushRsToListOfObjArray(securityDb, qs)) { + String key = (String) row[0]; + if (key != null && PREDEFINED_FEATURE_KEYS.contains(key)) { + Object enabledVal = row[1]; + boolean enabled = enabledVal instanceof Boolean ? (Boolean) enabledVal + : (enabledVal != null && "true".equalsIgnoreCase(enabledVal.toString())); + result.put(key, enabled); } - } catch (Exception e) { - classLogger.error("Failed to get platform profile features", e); } return result; } @@ -283,7 +261,7 @@ public static Map getProfileFeatures(String profileId) { /** Assigns a user to a platform profile, replacing any existing assignment for that user. */ public static void assignUserProfile(String userId, String profileId, User actor) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - String actorId = prerna.reactor.appprofile.AppProfileUtils.getUserId(actor); + String actorId = getUserId(actor); PreparedStatement ps = null; try { ps = securityDb.getPreparedStatement("DELETE FROM PLATFORM_USER_PROFILE WHERE USER_ID=?"); @@ -338,7 +316,7 @@ public static void removeUserProfile(String userId, User actor) { */ public static Map getUserFeatures(User user) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - String userId = prerna.reactor.appprofile.AppProfileUtils.getUserId(user); + String userId = getUserId(user); String profileId = getAssignedProfileId(securityDb, userId); if (profileId == null) { Map all = new LinkedHashMap<>(); @@ -353,36 +331,27 @@ public static Map getUserFeatures(User user) { /** Returns the list of users assigned to the given platform profile with their name, email, and assignment metadata. */ public static List> getPlatformProfileUsers(String profileId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - List> users = new ArrayList<>(); SelectQueryStruct qs = new SelectQueryStruct(); - qs.addSelector(new QueryColumnSelector("PLATFORM_USER_PROFILE__USER_ID")); - qs.addSelector(new QueryColumnSelector("SMSS_USER__NAME")); - qs.addSelector(new QueryColumnSelector("SMSS_USER__EMAIL")); - qs.addSelector(new QueryColumnSelector("PLATFORM_USER_PROFILE__ASSIGNED_BY")); - qs.addSelector(new QueryColumnSelector("PLATFORM_USER_PROFILE__ASSIGNED_AT")); + qs.addSelector(new QueryColumnSelector("PLATFORM_USER_PROFILE__USER_ID", "userId")); + qs.addSelector(new QueryColumnSelector("SMSS_USER__NAME", "name")); + qs.addSelector(new QueryColumnSelector("SMSS_USER__EMAIL", "email")); + qs.addSelector(new QueryColumnSelector("PLATFORM_USER_PROFILE__ASSIGNED_BY", "assignedBy")); + qs.addSelector(new QueryColumnSelector("PLATFORM_USER_PROFILE__ASSIGNED_AT", "assignedAt")); qs.addRelation("PLATFORM_USER_PROFILE__USER_ID", "SMSS_USER__ID", "left.join"); qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("PLATFORM_USER_PROFILE__PROFILE_ID", "==", profileId)); - try (IRawSelectWrapper wrapper = WrapperManager.getInstance().getRawWrapper(securityDb, qs)) { - while (wrapper.hasNext()) { - Object[] row = wrapper.next().getValues(); - Map user = new HashMap<>(); - user.put("userId", row[0]); - user.put("name", row[1]); - user.put("email", row[2]); - user.put("assignedBy", row[3]); - user.put("assignedAt", row[4]); - users.add(user); - } - } catch (Exception e) { - classLogger.error("Failed to get platform profile users", e); - } - return users; + return QueryExecutionUtility.flushRsToMap(securityDb, qs); } // ─── Private helpers ───────────────────────────────────────────────────── + private static String getUserId(User user) { + if (user == null) return null; + AccessToken token = user.getAccessToken(user.getPrimaryLogin()); + return token != null ? token.getId() : null; + } + private static int getAssignedUserCount(IRDBMSEngine securityDb, String profileId) { SelectQueryStruct qs = new SelectQueryStruct(); QueryFunctionSelector countFn = new QueryFunctionSelector(); @@ -390,30 +359,14 @@ private static int getAssignedUserCount(IRDBMSEngine securityDb, String profileI countFn.setFunction(QueryFunctionHelper.COUNT); qs.addSelector(countFn); qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("PLATFORM_USER_PROFILE__PROFILE_ID", "==", profileId)); - - try (IRawSelectWrapper wrapper = WrapperManager.getInstance().getRawWrapper(securityDb, qs)) { - if (wrapper.hasNext()) { - Object val = wrapper.next().getValues()[0]; - return val instanceof Number ? ((Number) val).intValue() : 0; - } - } catch (Exception e) { - classLogger.error("Failed to count platform profile users", e); - } - return 0; + Integer count = QueryExecutionUtility.flushToInteger(securityDb, qs); + return count != null ? count : 0; } private static String getAssignedProfileId(IRDBMSEngine securityDb, String userId) { SelectQueryStruct qs = new SelectQueryStruct(); qs.addSelector(new QueryColumnSelector("PLATFORM_USER_PROFILE__PROFILE_ID")); qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("PLATFORM_USER_PROFILE__USER_ID", "==", userId)); - - try (IRawSelectWrapper wrapper = WrapperManager.getInstance().getRawWrapper(securityDb, qs)) { - if (wrapper.hasNext()) { - return (String) wrapper.next().getValues()[0]; - } - } catch (Exception e) { - classLogger.error("Failed to get platform user profile", e); - } - return null; + return QueryExecutionUtility.flushToString(securityDb, qs); } } diff --git a/src/prerna/reactor/platformprofile/RemoveUserPlatformProfileReactor.java b/src/prerna/reactor/platformprofile/RemoveUserPlatformProfileReactor.java index e7cfd3f4fb1..2969904dd6a 100644 --- a/src/prerna/reactor/platformprofile/RemoveUserPlatformProfileReactor.java +++ b/src/prerna/reactor/platformprofile/RemoveUserPlatformProfileReactor.java @@ -28,6 +28,7 @@ package prerna.reactor.platformprofile; import prerna.auth.User; +import prerna.auth.utils.SecurityAdminUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; @@ -50,7 +51,7 @@ public RemoveUserPlatformProfileReactor() { public NounMetadata execute() { organizeKeys(); User user = this.insight.getUser(); - if (!PlatformProfileUtils.canManage(user)) { + if (!SecurityAdminUtils.userIsAdmin(user)) { throw new IllegalArgumentException("User must be an admin to manage platform profiles."); } String userId = this.keyValue.get(ReactorKeysEnum.USER_ID.getKey()); diff --git a/src/prerna/reactor/platformprofile/SetPlatformFeatureReactor.java b/src/prerna/reactor/platformprofile/SetPlatformFeatureReactor.java index b704f648de9..a808ef41a8e 100644 --- a/src/prerna/reactor/platformprofile/SetPlatformFeatureReactor.java +++ b/src/prerna/reactor/platformprofile/SetPlatformFeatureReactor.java @@ -28,6 +28,7 @@ package prerna.reactor.platformprofile; import prerna.auth.User; +import prerna.auth.utils.SecurityAdminUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; @@ -50,7 +51,7 @@ public SetPlatformFeatureReactor() { public NounMetadata execute() { organizeKeys(); User user = this.insight.getUser(); - if (!PlatformProfileUtils.canManage(user)) { + if (!SecurityAdminUtils.userIsAdmin(user)) { throw new IllegalArgumentException("User must be an admin to manage platform profiles."); } String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); diff --git a/src/prerna/reactor/platformprofile/UpdatePlatformProfileReactor.java b/src/prerna/reactor/platformprofile/UpdatePlatformProfileReactor.java index 6ee70d0235d..83fa1e68ba1 100644 --- a/src/prerna/reactor/platformprofile/UpdatePlatformProfileReactor.java +++ b/src/prerna/reactor/platformprofile/UpdatePlatformProfileReactor.java @@ -28,6 +28,7 @@ package prerna.reactor.platformprofile; import prerna.auth.User; +import prerna.auth.utils.SecurityAdminUtils; import prerna.reactor.AbstractReactor; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; @@ -50,7 +51,7 @@ public UpdatePlatformProfileReactor() { public NounMetadata execute() { organizeKeys(); User user = this.insight.getUser(); - if (!PlatformProfileUtils.canManage(user)) { + if (!SecurityAdminUtils.userIsAdmin(user)) { throw new IllegalArgumentException("User must be an admin to manage platform profiles."); } String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); From 391ade8065647894902f0f5f36c517d2035b59e4 Mon Sep 17 00:00:00 2001 From: "Patel, Parth" Date: Fri, 26 Jun 2026 12:43:17 -0400 Subject: [PATCH 8/9] feat: bulk user assignment for Assign reactors (assigned/skipped/errors) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AssignAppUserProfile, AssignAppUserSubgroup, and AssignUserPlatformProfile now accept a list of userIds so the frontend can batch-assign in one call. Each user is processed independently and the response buckets assigned, skipped (already in profile), and errors (user not found or DB error). Platform profiles remain one-per-user by design — assigning a user already in a different platform profile replaces that assignment (DELETE + INSERT). App profiles continue to be additive — a user can be in multiple profiles. Co-Authored-By: Claude Sonnet 4.6 --- .../reactor/appprofile/AppProfileUtils.java | 150 ++++++++++++++++++ .../AssignAppUserSubgroupReactor.java | 53 ++++++- .../user/AssignAppUserProfileReactor.java | 54 ++++++- .../AssignUserPlatformProfileReactor.java | 57 ++++++- .../platformprofile/PlatformProfileUtils.java | 90 +++++++++++ 5 files changed, 381 insertions(+), 23 deletions(-) diff --git a/src/prerna/reactor/appprofile/AppProfileUtils.java b/src/prerna/reactor/appprofile/AppProfileUtils.java index 2a0941c8bd8..2b8c670de8f 100644 --- a/src/prerna/reactor/appprofile/AppProfileUtils.java +++ b/src/prerna/reactor/appprofile/AppProfileUtils.java @@ -509,6 +509,20 @@ public static List> getProfileFeatures(String appId, String // ─── User-Profile Assignment (multi-profile) ──────────────────────────── + /** + * Returns {@code true} if the given userId exists as a row in {@code SMSS_USER}. + */ + private static boolean userExists(IRDBMSEngine securityDb, String userId) { + SelectQueryStruct qs = new SelectQueryStruct(); + QueryFunctionSelector countFn = new QueryFunctionSelector(); + countFn.addInnerSelector(new QueryColumnSelector("SMSS_USER__ID")); + countFn.setFunction(QueryFunctionHelper.COUNT); + qs.addSelector(countFn); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("SMSS_USER__ID", "==", userId)); + Integer count = QueryExecutionUtility.flushToInteger(securityDb, qs); + return count != null && count > 0; + } + /** * Assigns a user to a profile. A user can be in multiple profiles simultaneously. * If already assigned to this specific profile, this is a no-op. @@ -547,6 +561,74 @@ public static void assignUserProfile(String appId, String userId, String profile } } + /** + * Bulk-assigns a list of users to a profile for an app. Each user is processed independently: + * users already in the profile are silently skipped, users not found in {@code SMSS_USER} are + * recorded in the errors bucket. A user may be in multiple profiles simultaneously. + * + * @param appId the app ID + * @param userIds non-empty list of {@code SMSS_USER.ID} values to assign + * @param profileId the profile to assign users to + * @param actor the user performing the assignment + * @return a map with keys: {@code assigned} ({@code List}), {@code skipped} + * ({@code List}), {@code errors} ({@code Map}) + */ + public static Map assignUsersToProfile(String appId, List userIds, String profileId, User actor) { + List assigned = new ArrayList<>(); + List skipped = new ArrayList<>(); + Map errors = new LinkedHashMap<>(); + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + String actorId = getUserId(actor); + + for (String userId : userIds) { + if (!userExists(securityDb, userId)) { + classLogger.warn("assignUsersToProfile: user '{}' not found in SMSS_USER — adding to errors", userId); + errors.put(userId, "User not found."); + continue; + } + + SelectQueryStruct checkQs = new SelectQueryStruct(); + QueryFunctionSelector countFn = new QueryFunctionSelector(); + countFn.addInnerSelector(new QueryColumnSelector("APP_USER_PROFILE__USER_ID")); + countFn.setFunction(QueryFunctionHelper.COUNT); + checkQs.addSelector(countFn); + checkQs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_PROFILE__APP_ID", "==", appId)); + checkQs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_PROFILE__USER_ID", "==", userId)); + checkQs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_PROFILE__PROFILE_ID", "==", profileId)); + Integer existingCount = QueryExecutionUtility.flushToInteger(securityDb, checkQs); + if (existingCount != null && existingCount > 0) { + classLogger.debug("assignUsersToProfile: user '{}' already in profile '{}' for app '{}' — skipping", userId, profileId, appId); + skipped.add(userId); + continue; + } + + PreparedStatement ps = null; + try { + ps = securityDb.getPreparedStatement( + "INSERT INTO APP_USER_PROFILE (APP_ID, USER_ID, PROFILE_ID, ASSIGNED_BY, ASSIGNED_AT) VALUES (?,?,?,?,?)"); + ps.setString(1, appId); + ps.setString(2, userId); + ps.setString(3, profileId); + ps.setString(4, actorId); + ps.setTimestamp(5, Utility.getCurrentSqlTimestampUTC()); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + assigned.add(userId); + } catch (SQLException e) { + classLogger.error("assignUsersToProfile: DB error assigning user '{}'", userId, e); + errors.put(userId, "Database error during assignment."); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + } + + Map result = new LinkedHashMap<>(); + result.put("assigned", assigned); + result.put("skipped", skipped); + result.put("errors", errors); + return result; + } + /** * Removes ALL profile assignments for a user from an app (used when removing user from app entirely). */ @@ -934,6 +1016,74 @@ public static void assignUserSubgroup(String appId, String userId, String subgro } } + /** + * Bulk-assigns a list of users to a sub-group. Each user is processed independently: + * users already in the sub-group are silently skipped, users not found in {@code SMSS_USER} + * are recorded in the errors bucket. + * + * @param appId the app ID + * @param userIds non-empty list of {@code SMSS_USER.ID} values to assign + * @param subgroupId the sub-group to assign users to + * @param actor the user performing the assignment + * @return a map with keys: {@code assigned} ({@code List}), {@code skipped} + * ({@code List}), {@code errors} ({@code Map}) + */ + public static Map assignUsersToSubgroup(String appId, List userIds, String subgroupId, User actor) { + List assigned = new ArrayList<>(); + List skipped = new ArrayList<>(); + Map errors = new LinkedHashMap<>(); + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + String actorId = getUserId(actor); + + for (String userId : userIds) { + if (!userExists(securityDb, userId)) { + classLogger.warn("assignUsersToSubgroup: user '{}' not found in SMSS_USER — adding to errors", userId); + errors.put(userId, "User not found."); + continue; + } + + SelectQueryStruct checkQs = new SelectQueryStruct(); + QueryFunctionSelector countFn = new QueryFunctionSelector(); + countFn.addInnerSelector(new QueryColumnSelector("APP_USER_SUBGROUP__USER_ID")); + countFn.setFunction(QueryFunctionHelper.COUNT); + checkQs.addSelector(countFn); + checkQs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_SUBGROUP__APP_ID", "==", appId)); + checkQs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_SUBGROUP__USER_ID", "==", userId)); + checkQs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_SUBGROUP__SUBGROUP_ID", "==", subgroupId)); + Integer existingCount = QueryExecutionUtility.flushToInteger(securityDb, checkQs); + if (existingCount != null && existingCount > 0) { + classLogger.debug("assignUsersToSubgroup: user '{}' already in subgroup '{}' for app '{}' — skipping", userId, subgroupId, appId); + skipped.add(userId); + continue; + } + + PreparedStatement ps = null; + try { + ps = securityDb.getPreparedStatement( + "INSERT INTO APP_USER_SUBGROUP (APP_ID, USER_ID, SUBGROUP_ID, ASSIGNED_BY, ASSIGNED_AT) VALUES (?,?,?,?,?)"); + ps.setString(1, appId); + ps.setString(2, userId); + ps.setString(3, subgroupId); + ps.setString(4, actorId); + ps.setTimestamp(5, Utility.getCurrentSqlTimestampUTC()); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + assigned.add(userId); + } catch (SQLException e) { + classLogger.error("assignUsersToSubgroup: DB error assigning user '{}'", userId, e); + errors.put(userId, "Database error during assignment."); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + } + + Map result = new LinkedHashMap<>(); + result.put("assigned", assigned); + result.put("skipped", skipped); + result.put("errors", errors); + return result; + } + /** * Removes a user from a specific sub-group assignment. */ diff --git a/src/prerna/reactor/appprofile/subgroup/AssignAppUserSubgroupReactor.java b/src/prerna/reactor/appprofile/subgroup/AssignAppUserSubgroupReactor.java index 9a93fcaf48f..20d2cf3da40 100644 --- a/src/prerna/reactor/appprofile/subgroup/AssignAppUserSubgroupReactor.java +++ b/src/prerna/reactor/appprofile/subgroup/AssignAppUserSubgroupReactor.java @@ -27,21 +27,28 @@ *******************************************************************************/ package prerna.reactor.appprofile.subgroup; -import prerna.reactor.appprofile.AppProfileUtils; +import java.util.List; +import java.util.Map; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; + import prerna.auth.User; import prerna.reactor.AbstractReactor; +import prerna.reactor.appprofile.AppProfileUtils; +import prerna.sablecc2.om.GenRowStruct; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; /** - * Assign a user to a sub-group. + * Bulk-assigns one or more users to a sub-group. Users already in the sub-group are + * silently skipped; unknown users are returned in the {@code errors} bucket. * - *

Pixel: {@code AssignAppUserSubgroup(app=["appId"], userId=["userId"], subgroup=["subgroupId"]);}

+ *

Pixel: {@code AssignAppUserSubgroup(app=["appId"], userId=["user1", "user2"], subgroup=["subgroupId"]);}

+ * + *

Returns a map with three keys: {@code assigned}, {@code skipped}, {@code errors}.

*/ public class AssignAppUserSubgroupReactor extends AbstractReactor { @@ -57,20 +64,50 @@ public NounMetadata execute() { organizeKeys(); User user = this.insight.getUser(); String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); - String userId = this.keyValue.get(ReactorKeysEnum.USER_ID.getKey()); String subgroupId = this.keyValue.get(ReactorKeysEnum.SUBGROUP_ID.getKey()); + List userIds = getUserIds(); if (!AppProfileUtils.canAssignProfiles(user, appId)) { throw new IllegalArgumentException("User does not have permission to assign users for this app."); } - AppProfileUtils.assignUserSubgroup(appId, userId, subgroupId, user); - NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); - noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("User assigned to subgroup.")); + + Map result = AppProfileUtils.assignUsersToSubgroup(appId, userIds, subgroupId, user); + classLogger.debug("AssignAppUserSubgroup: assigned={}, skipped={}, errors={}", + ((List) result.get("assigned")).size(), + ((List) result.get("skipped")).size(), + ((Map) result.get("errors")).size()); + + NounMetadata noun = new NounMetadata(result, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage( + "Bulk assign complete: " + ((List) result.get("assigned")).size() + " assigned, " + + ((List) result.get("skipped")).size() + " skipped, " + + ((Map) result.get("errors")).size() + " errors.")); return noun; } + /** + * Reads the {@code userId} parameter as a list. Validates that the list is non-empty + * and that every item is a non-blank string. + */ + private List getUserIds() { + GenRowStruct grs = this.store.getGenRowStruct(ReactorKeysEnum.USER_ID.getKey()); + if (grs == null || grs.isEmpty()) { + throw new IllegalArgumentException("userId must be provided and must contain at least one value."); + } + List userIds = grs.getAllStrValues(); + if (userIds == null || userIds.isEmpty()) { + throw new IllegalArgumentException("userId must contain at least one value."); + } + for (String id : userIds) { + if (id == null || id.trim().isEmpty()) { + throw new IllegalArgumentException("userId list contains a blank entry — all userId values must be non-blank."); + } + } + return userIds; + } + @Override public String getReactorDescription() { - return "Assign a user to a sub-group."; + return "Bulk-assign one or more users to a sub-group. Returns assigned, skipped, and errors buckets."; } } diff --git a/src/prerna/reactor/appprofile/user/AssignAppUserProfileReactor.java b/src/prerna/reactor/appprofile/user/AssignAppUserProfileReactor.java index a2ed85c7def..bf042d73459 100644 --- a/src/prerna/reactor/appprofile/user/AssignAppUserProfileReactor.java +++ b/src/prerna/reactor/appprofile/user/AssignAppUserProfileReactor.java @@ -27,21 +27,29 @@ *******************************************************************************/ package prerna.reactor.appprofile.user; -import prerna.reactor.appprofile.AppProfileUtils; +import java.util.List; +import java.util.Map; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; + import prerna.auth.User; import prerna.reactor.AbstractReactor; +import prerna.reactor.appprofile.AppProfileUtils; +import prerna.sablecc2.om.GenRowStruct; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; /** - * Assign a user to a profile for an app. A user can be in multiple profiles simultaneously. + * Bulk-assigns one or more users to a profile for an app. A user can be in multiple profiles + * simultaneously. Users already in the profile are silently skipped; unknown users are returned + * in the {@code errors} bucket. * - *

Pixel: {@code AssignAppUserProfile(app=["appId"], userId=["userId"], profile=["profileId"]);}

+ *

Pixel: {@code AssignAppUserProfile(app=["appId"], userId=["user1", "user2"], profile=["profileId"]);}

+ * + *

Returns a map with three keys: {@code assigned}, {@code skipped}, {@code errors}.

*/ public class AssignAppUserProfileReactor extends AbstractReactor { @@ -57,20 +65,50 @@ public NounMetadata execute() { organizeKeys(); User user = this.insight.getUser(); String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); - String userId = this.keyValue.get(ReactorKeysEnum.USER_ID.getKey()); String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); + List userIds = getUserIds(); if (!AppProfileUtils.canAssignProfiles(user, appId)) { throw new IllegalArgumentException("User does not have permission to assign profiles for this app."); } - AppProfileUtils.assignUserProfile(appId, userId, profileId, user); - NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); - noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("User assigned to profile.")); + + Map result = AppProfileUtils.assignUsersToProfile(appId, userIds, profileId, user); + classLogger.debug("AssignAppUserProfile: assigned={}, skipped={}, errors={}", + ((List) result.get("assigned")).size(), + ((List) result.get("skipped")).size(), + ((Map) result.get("errors")).size()); + + NounMetadata noun = new NounMetadata(result, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage( + "Bulk assign complete: " + ((List) result.get("assigned")).size() + " assigned, " + + ((List) result.get("skipped")).size() + " skipped, " + + ((Map) result.get("errors")).size() + " errors.")); return noun; } + /** + * Reads the {@code userId} parameter as a list. Validates that the list is non-empty + * and that every item is a non-blank string. + */ + private List getUserIds() { + GenRowStruct grs = this.store.getGenRowStruct(ReactorKeysEnum.USER_ID.getKey()); + if (grs == null || grs.isEmpty()) { + throw new IllegalArgumentException("userId must be provided and must contain at least one value."); + } + List userIds = grs.getAllStrValues(); + if (userIds == null || userIds.isEmpty()) { + throw new IllegalArgumentException("userId must contain at least one value."); + } + for (String id : userIds) { + if (id == null || id.trim().isEmpty()) { + throw new IllegalArgumentException("userId list contains a blank entry — all userId values must be non-blank."); + } + } + return userIds; + } + @Override public String getReactorDescription() { - return "Assign a user to a profile for an app. A user can be in multiple profiles simultaneously."; + return "Bulk-assign one or more users to a profile for an app. Returns assigned, skipped, and errors buckets."; } } diff --git a/src/prerna/reactor/platformprofile/AssignUserPlatformProfileReactor.java b/src/prerna/reactor/platformprofile/AssignUserPlatformProfileReactor.java index 4b7a50646ed..9c55382f8b7 100644 --- a/src/prerna/reactor/platformprofile/AssignUserPlatformProfileReactor.java +++ b/src/prerna/reactor/platformprofile/AssignUserPlatformProfileReactor.java @@ -27,21 +27,34 @@ *******************************************************************************/ package prerna.reactor.platformprofile; +import java.util.List; +import java.util.Map; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import prerna.auth.User; import prerna.auth.utils.SecurityAdminUtils; import prerna.reactor.AbstractReactor; +import prerna.sablecc2.om.GenRowStruct; import prerna.sablecc2.om.PixelDataType; import prerna.sablecc2.om.PixelOperationType; import prerna.sablecc2.om.ReactorKeysEnum; import prerna.sablecc2.om.nounmeta.NounMetadata; /** - * Assigns a user to a platform profile, replacing any existing profile assignment for that user. + * Bulk-assigns one or more users to a platform profile. Because a user may be in at most + * one platform profile at a time, users already in this profile are silently skipped; users + * in a different profile are re-assigned; unknown users are returned in the {@code errors} bucket. + * + *

Pixel: {@code AssignUserPlatformProfile(userId=["user1", "user2"], profileId=[""]);}

* - *

Pixel: {@code AssignUserPlatformProfile(userId=[""], profileId=[""]);}

+ *

Returns a map with three keys: {@code assigned}, {@code skipped}, {@code errors}.

*/ public class AssignUserPlatformProfileReactor extends AbstractReactor { + private static final Logger classLogger = LogManager.getLogger(AssignUserPlatformProfileReactor.class); + public AssignUserPlatformProfileReactor() { this.keysToGet = new String[] { ReactorKeysEnum.USER_ID.getKey(), ReactorKeysEnum.PROFILE_ID.getKey() }; this.keyRequired = new int[] { 1, 1 }; @@ -54,16 +67,46 @@ public NounMetadata execute() { if (!SecurityAdminUtils.userIsAdmin(user)) { throw new IllegalArgumentException("User must be an admin to manage platform profiles."); } - String userId = this.keyValue.get(ReactorKeysEnum.USER_ID.getKey()); String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); - PlatformProfileUtils.assignUserProfile(userId, profileId, user); - NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); - noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("User assigned to platform profile.")); + List userIds = getUserIds(); + + Map result = PlatformProfileUtils.assignUsersToProfile(userIds, profileId, user); + classLogger.debug("AssignUserPlatformProfile: assigned={}, skipped={}, errors={}", + ((List) result.get("assigned")).size(), + ((List) result.get("skipped")).size(), + ((Map) result.get("errors")).size()); + + NounMetadata noun = new NounMetadata(result, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage( + "Bulk assign complete: " + ((List) result.get("assigned")).size() + " assigned, " + + ((List) result.get("skipped")).size() + " skipped, " + + ((Map) result.get("errors")).size() + " errors.")); return noun; } + /** + * Reads the {@code userId} parameter as a list. Validates that the list is non-empty + * and that every item is a non-blank string. + */ + private List getUserIds() { + GenRowStruct grs = this.store.getGenRowStruct(ReactorKeysEnum.USER_ID.getKey()); + if (grs == null || grs.isEmpty()) { + throw new IllegalArgumentException("userId must be provided and must contain at least one value."); + } + List userIds = grs.getAllStrValues(); + if (userIds == null || userIds.isEmpty()) { + throw new IllegalArgumentException("userId must contain at least one value."); + } + for (String id : userIds) { + if (id == null || id.trim().isEmpty()) { + throw new IllegalArgumentException("userId list contains a blank entry — all userId values must be non-blank."); + } + } + return userIds; + } + @Override public String getReactorDescription() { - return "Assign a user to a platform profile."; + return "Bulk-assign one or more users to a platform profile. Returns assigned, skipped, and errors buckets."; } } diff --git a/src/prerna/reactor/platformprofile/PlatformProfileUtils.java b/src/prerna/reactor/platformprofile/PlatformProfileUtils.java index 4236e12884f..8f565322f56 100644 --- a/src/prerna/reactor/platformprofile/PlatformProfileUtils.java +++ b/src/prerna/reactor/platformprofile/PlatformProfileUtils.java @@ -258,6 +258,20 @@ public static Map getProfileFeatures(String profileId) { // ─── User-Profile Assignment ───────────────────────────────────────────── + /** + * Returns {@code true} if the given userId exists as a row in {@code SMSS_USER}. + */ + private static boolean userExists(IRDBMSEngine securityDb, String userId) { + SelectQueryStruct qs = new SelectQueryStruct(); + QueryFunctionSelector countFn = new QueryFunctionSelector(); + countFn.addInnerSelector(new QueryColumnSelector("SMSS_USER__ID")); + countFn.setFunction(QueryFunctionHelper.COUNT); + qs.addSelector(countFn); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("SMSS_USER__ID", "==", userId)); + Integer count = QueryExecutionUtility.flushToInteger(securityDb, qs); + return count != null && count > 0; + } + /** Assigns a user to a platform profile, replacing any existing assignment for that user. */ public static void assignUserProfile(String userId, String profileId, User actor) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); @@ -291,6 +305,82 @@ public static void assignUserProfile(String userId, String profileId, User actor } } + /** + * Bulk-assigns a list of users to a platform profile. Each user is processed independently. + * Because a user may be in at most one platform profile at a time, users already in this + * profile are silently skipped; users in a different profile are re-assigned (prior assignment + * removed first). Users not found in {@code SMSS_USER} are recorded in the errors bucket. + * + * @param userIds non-empty list of {@code SMSS_USER.ID} values to assign + * @param profileId the platform profile to assign users to + * @param actor the user performing the assignment + * @return a map with keys: {@code assigned} ({@code List}), {@code skipped} + * ({@code List}), {@code errors} ({@code Map}) + */ + public static Map assignUsersToProfile(List userIds, String profileId, User actor) { + List assigned = new ArrayList<>(); + List skipped = new ArrayList<>(); + Map errors = new LinkedHashMap<>(); + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + String actorId = getUserId(actor); + + for (String userId : userIds) { + if (!userExists(securityDb, userId)) { + classLogger.warn("assignUsersToProfile: user '{}' not found in SMSS_USER — adding to errors", userId); + errors.put(userId, "User not found."); + continue; + } + + String currentProfileId = getAssignedProfileId(securityDb, userId); + if (profileId.equals(currentProfileId)) { + classLogger.debug("assignUsersToProfile: user '{}' already in platform profile '{}' — skipping", userId, profileId); + skipped.add(userId); + continue; + } + + // Remove any existing platform profile assignment before inserting the new one + if (currentProfileId != null) { + PreparedStatement delPs = null; + try { + delPs = securityDb.getPreparedStatement("DELETE FROM PLATFORM_USER_PROFILE WHERE USER_ID=?"); + delPs.setString(1, userId); + delPs.execute(); + if (!delPs.getConnection().getAutoCommit()) delPs.getConnection().commit(); + } catch (SQLException e) { + classLogger.error("assignUsersToProfile: DB error removing existing assignment for user '{}'", userId, e); + errors.put(userId, "Database error removing existing assignment."); + continue; + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, delPs); + } + } + + PreparedStatement ps = null; + try { + ps = securityDb.getPreparedStatement( + "INSERT INTO PLATFORM_USER_PROFILE (USER_ID, PROFILE_ID, ASSIGNED_BY, ASSIGNED_AT) VALUES (?,?,?,?)"); + ps.setString(1, userId); + ps.setString(2, profileId); + ps.setString(3, actorId); + ps.setTimestamp(4, Utility.getCurrentSqlTimestampUTC()); + ps.execute(); + if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); + assigned.add(userId); + } catch (SQLException e) { + classLogger.error("assignUsersToProfile: DB error assigning user '{}'", userId, e); + errors.put(userId, "Database error during assignment."); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + } + + Map result = new LinkedHashMap<>(); + result.put("assigned", assigned); + result.put("skipped", skipped); + result.put("errors", errors); + return result; + } + /** Removes the platform profile assignment for the specified user. */ public static void removeUserProfile(String userId, User actor) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); From 90eb7e2023fd3a879443f89cf545c3491aa9eaa0 Mon Sep 17 00:00:00 2001 From: "Patel, Parth" Date: Fri, 26 Jun 2026 14:33:11 -0400 Subject: [PATCH 9/9] refactor(appprofile): remove group/subgroup model, flatten app profiles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes the entire IS_GROUP/subgroup hierarchy from the app profile system. All profiles are now flat — features and users are managed directly on the profile, with no subgroup layer. Deleted (9 files): - subgroup/AssignAppUserSubgroupReactor.java - subgroup/CreateAppSubgroupReactor.java - subgroup/DeleteAppSubgroupReactor.java - subgroup/GetAppSubgroupFeaturesReactor.java - subgroup/GetAppSubgroupUsersReactor.java - subgroup/GetAppSubgroupsReactor.java - subgroup/RemoveAppUserSubgroupReactor.java - subgroup/SetAppSubgroupFeatureReactor.java - subgroup/UpdateAppSubgroupReactor.java Modified: - AppProfileUtils: removed all subgroup CRUD/query methods; createProfile/ updateProfile signatures drop isGroup param (always writes false to DB); getProfiles removes IS_GROUP from SELECT/GROUP BY; deleteProfile removes subgroup cascade; getUserFeatures returns Map; feature resolution is now explicit-profile → default-profile (no subgroup step); getExplicitUserProfiles rewritten as two-query in-memory join; adds duplicate profile name guard on createProfile; removes canEvaluateFeatures - CreateAppProfileReactor: removed IS_GROUP key from keysToGet/keyRequired - UpdateAppProfileReactor: removed IS_GROUP key from keysToGet/keyRequired - CheckAppFeatureReactor, CheckFeatureReactor, GetAppUserFeaturesReactor: removed canEvaluateFeatures access guard (any app viewer can evaluate) - GetUserAppProfilesReactor: updated return type to List>, removed canEvaluateFeatures guard Co-Authored-By: Claude Sonnet 4.6 --- .../reactor/appprofile/AppProfileUtils.java | 776 +++--------------- .../feature/CheckAppFeatureReactor.java | 3 - .../feature/CheckFeatureReactor.java | 3 - .../feature/GetAppUserFeaturesReactor.java | 5 +- .../profile/CreateAppProfileReactor.java | 7 +- .../profile/UpdateAppProfileReactor.java | 8 +- .../AssignAppUserSubgroupReactor.java | 113 --- .../subgroup/CreateAppSubgroupReactor.java | 79 -- .../subgroup/DeleteAppSubgroupReactor.java | 75 -- .../GetAppSubgroupFeaturesReactor.java | 77 -- .../subgroup/GetAppSubgroupUsersReactor.java | 77 -- .../subgroup/GetAppSubgroupsReactor.java | 76 -- .../RemoveAppUserSubgroupReactor.java | 76 -- .../SetAppSubgroupFeatureReactor.java | 78 -- .../subgroup/UpdateAppSubgroupReactor.java | 77 -- .../user/GetUserAppProfilesReactor.java | 6 +- 16 files changed, 116 insertions(+), 1420 deletions(-) delete mode 100644 src/prerna/reactor/appprofile/subgroup/AssignAppUserSubgroupReactor.java delete mode 100644 src/prerna/reactor/appprofile/subgroup/CreateAppSubgroupReactor.java delete mode 100644 src/prerna/reactor/appprofile/subgroup/DeleteAppSubgroupReactor.java delete mode 100644 src/prerna/reactor/appprofile/subgroup/GetAppSubgroupFeaturesReactor.java delete mode 100644 src/prerna/reactor/appprofile/subgroup/GetAppSubgroupUsersReactor.java delete mode 100644 src/prerna/reactor/appprofile/subgroup/GetAppSubgroupsReactor.java delete mode 100644 src/prerna/reactor/appprofile/subgroup/RemoveAppUserSubgroupReactor.java delete mode 100644 src/prerna/reactor/appprofile/subgroup/SetAppSubgroupFeatureReactor.java delete mode 100644 src/prerna/reactor/appprofile/subgroup/UpdateAppSubgroupReactor.java diff --git a/src/prerna/reactor/appprofile/AppProfileUtils.java b/src/prerna/reactor/appprofile/AppProfileUtils.java index 2b8c670de8f..9829a65253c 100644 --- a/src/prerna/reactor/appprofile/AppProfileUtils.java +++ b/src/prerna/reactor/appprofile/AppProfileUtils.java @@ -31,10 +31,13 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -101,16 +104,6 @@ public static boolean canAssignProfiles(User user, String appId) { return count != null && count > 0; } - /** - * Returns true if the user can evaluate features for the given app (admin, - * profile manager, or any project viewer). - */ - public static boolean canEvaluateFeatures(User user, String appId) { - if (SecurityAdminUtils.userIsAdmin(user)) return true; - if (canAssignProfiles(user, appId)) return true; - return SecurityProjectUtils.userCanViewProject(user, appId); - } - private static boolean appExists(String appId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); SelectQueryStruct qs = new SelectQueryStruct(); @@ -125,7 +118,7 @@ private static boolean appExists(String appId) { * Creates a new named profile for an app and returns its metadata map. */ public static Map createProfile(String appId, String name, String description, - boolean isDefault, boolean isGroup, User user) { + boolean isDefault, User user) { if (name == null || name.trim().isEmpty()) { throw new IllegalArgumentException("Profile name cannot be blank."); } @@ -137,6 +130,14 @@ public static Map createProfile(String appId, String name, Strin String actorId = getUserId(user); IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + SelectQueryStruct dupQs = new SelectQueryStruct(); + dupQs.addSelector(new QueryColumnSelector("APP_PROFILE__PROFILE_ID", "profileId")); + dupQs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE__APP_ID", "==", appId)); + dupQs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE__PROFILE_NAME", "==", profileName)); + if (QueryExecutionUtility.flushToString(securityDb, dupQs) != null) { + throw new IllegalArgumentException("A profile named '" + profileName + "' already exists for this app."); + } + if (isDefault) { clearDefaultProfile(securityDb, appId); } @@ -151,7 +152,7 @@ public static Map createProfile(String appId, String name, Strin ps.setString(i++, profileName); ps.setString(i++, description); ps.setBoolean(i++, isDefault); - ps.setBoolean(i++, isGroup); + ps.setBoolean(i++, false); ps.setString(i++, actorId); ps.setTimestamp(i++, Utility.getCurrentSqlTimestampUTC()); ps.execute(); @@ -170,16 +171,15 @@ public static Map createProfile(String appId, String name, Strin result.put("profileName", profileName); result.put("description", description); result.put("isDefault", isDefault); - result.put("isGroup", isGroup); return result; } /** * Updates mutable fields on an existing app profile (name, description, - * isDefault, isGroup). Null parameters are ignored. + * isDefault). Null parameters are ignored. */ public static void updateProfile(String appId, String profileId, String name, String description, - Boolean isDefault, Boolean isGroup, User user) { + Boolean isDefault, User user) { if (name != null) { if (name.trim().isEmpty()) throw new IllegalArgumentException("Profile name cannot be blank."); if (name.trim().length() > 100) throw new IllegalArgumentException("Profile name cannot exceed 100 characters."); @@ -193,7 +193,6 @@ public static void updateProfile(String appId, String profileId, String name, St if (name != null) { sb.append(" PROFILE_NAME=?,"); params.add(name.trim()); } if (description != null) { sb.append(" DESCRIPTION=?,"); params.add(description); } if (isDefault != null) { sb.append(" IS_DEFAULT=?,"); params.add(isDefault); } - if (isGroup != null) { sb.append(" IS_GROUP=?,"); params.add(isGroup); } if (params.isEmpty()) return; sb.setLength(sb.length() - 1); sb.append(" WHERE PROFILE_ID=? AND APP_ID=?"); @@ -224,8 +223,8 @@ public static void updateProfile(String appId, String profileId, String name, St } /** - * Deletes a profile and cascades to its feature mappings, subgroups, and - * subgroup assignments. Throws if any users are still assigned. + * Deletes a profile and cascades to its feature mappings. + * Throws if any users are still assigned. */ public static void deleteProfile(String appId, String profileId, User user) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); @@ -259,23 +258,6 @@ public static void deleteProfile(String appId, String profileId, User user) { } finally { ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); } - // cascade delete subgroups - List subgroupIds = getSubgroupIdsForProfile(securityDb, appId, profileId); - for (String subgroupId : subgroupIds) { - deleteSubgroupInternal(securityDb, appId, subgroupId); - } - ps = null; - try { - ps = securityDb.getPreparedStatement("DELETE FROM APP_PROFILE_SUBGROUP WHERE PROFILE_ID=? AND APP_ID=?"); - ps.setString(1, profileId); - ps.setString(2, appId); - ps.execute(); - if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); - } catch (SQLException e) { - classLogger.error("Failed to cascade delete subgroups for profile {}", profileId, e); - } finally { - ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); - } } /** @@ -289,7 +271,6 @@ public static List> getProfiles(String appId) { qs.addSelector(new QueryColumnSelector("APP_PROFILE__PROFILE_NAME", "profileName")); qs.addSelector(new QueryColumnSelector("APP_PROFILE__DESCRIPTION", "description")); qs.addSelector(new QueryColumnSelector("APP_PROFILE__IS_DEFAULT", "isDefault")); - qs.addSelector(new QueryColumnSelector("APP_PROFILE__IS_GROUP", "isGroup")); qs.addSelector(new QueryColumnSelector("APP_PROFILE__CREATED_BY", "createdBy")); qs.addSelector(new QueryColumnSelector("APP_PROFILE__CREATED_AT", "createdAt")); qs.addSelector(QueryFunctionSelector.makeFunctionSelector(QueryFunctionHelper.COUNT, "APP_USER_PROFILE__USER_ID", "userCount")); @@ -299,15 +280,11 @@ public static List> getProfiles(String appId) { qs.addGroupBy(new QueryColumnSelector("APP_PROFILE__PROFILE_NAME")); qs.addGroupBy(new QueryColumnSelector("APP_PROFILE__DESCRIPTION")); qs.addGroupBy(new QueryColumnSelector("APP_PROFILE__IS_DEFAULT")); - qs.addGroupBy(new QueryColumnSelector("APP_PROFILE__IS_GROUP")); qs.addGroupBy(new QueryColumnSelector("APP_PROFILE__CREATED_BY")); qs.addGroupBy(new QueryColumnSelector("APP_PROFILE__CREATED_AT")); qs.addOrderBy(new QueryColumnOrderBySelector("APP_PROFILE__PROFILE_NAME")); List> profiles = QueryExecutionUtility.flushRsToMap(securityDb, qs); - profiles.forEach(p -> { - p.put("isDefault", Boolean.TRUE.equals(p.get("isDefault"))); - p.put("isGroup", Boolean.TRUE.equals(p.get("isGroup"))); - }); + profiles.forEach(p -> p.put("isDefault", Boolean.TRUE.equals(p.get("isDefault")))); return profiles; } @@ -382,8 +359,7 @@ public static void updateFeature(String appId, String featureId, String featureK } /** - * Deletes an app feature and cascades removal from profile and subgroup feature - * mapping tables. + * Deletes an app feature and cascades removal from the profile feature mapping table. */ public static void deleteFeature(String appId, String featureId, User user) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); @@ -411,18 +387,6 @@ public static void deleteFeature(String appId, String featureId, User user) { } finally { ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); } - ps = null; - try { - ps = securityDb.getPreparedStatement("DELETE FROM APP_SUBGROUP_FEATURE WHERE FEATURE_ID=? AND APP_ID=?"); - ps.setString(1, featureId); - ps.setString(2, appId); - ps.execute(); - if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); - } catch (SQLException e) { - classLogger.error("Failed to cascade delete feature from subgroup mappings", e); - } finally { - ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); - } } /** @@ -507,7 +471,7 @@ public static List> getProfileFeatures(String appId, String return results; } - // ─── User-Profile Assignment (multi-profile) ──────────────────────────── + // ─── User-Profile Assignment ──────────────────────────────────────────── /** * Returns {@code true} if the given userId exists as a row in {@code SMSS_USER}. @@ -524,12 +488,10 @@ private static boolean userExists(IRDBMSEngine securityDb, String userId) { } /** - * Assigns a user to a profile. A user can be in multiple profiles simultaneously. - * If already assigned to this specific profile, this is a no-op. + * Assigns a user to a profile. If already assigned to this specific profile, this is a no-op. */ public static void assignUserProfile(String appId, String userId, String profileId, User actor) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - // check for existing assignment to avoid duplicates SelectQueryStruct checkQs = new SelectQueryStruct(); QueryFunctionSelector countFn = new QueryFunctionSelector(); countFn.addInnerSelector(new QueryColumnSelector("APP_USER_PROFILE__USER_ID")); @@ -564,14 +526,9 @@ public static void assignUserProfile(String appId, String userId, String profile /** * Bulk-assigns a list of users to a profile for an app. Each user is processed independently: * users already in the profile are silently skipped, users not found in {@code SMSS_USER} are - * recorded in the errors bucket. A user may be in multiple profiles simultaneously. + * recorded in the errors bucket. * - * @param appId the app ID - * @param userIds non-empty list of {@code SMSS_USER.ID} values to assign - * @param profileId the profile to assign users to - * @param actor the user performing the assignment - * @return a map with keys: {@code assigned} ({@code List}), {@code skipped} - * ({@code List}), {@code errors} ({@code Map}) + * @return a map with keys: {@code assigned}, {@code skipped}, {@code errors} */ public static Map assignUsersToProfile(String appId, List userIds, String profileId, User actor) { List assigned = new ArrayList<>(); @@ -630,7 +587,7 @@ public static Map assignUsersToProfile(String appId, List> getUserProfiles(String appId, String userId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); return getExplicitUserProfiles(securityDb, appId, userId); } - /** - * Returns a structured summary of the calling user's profile memberships: - * - "profiles": list of directly-assigned standard profiles - * - "groups": map of parent profile name -> list of subgroup names the user is in - */ - public static Map getUserAppProfiles(String appId, User user) { - IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - String userId = getUserId(user); - - Map result = new LinkedHashMap<>(); - List> directProfiles = new ArrayList<>(); - Map> groupMemberships = new LinkedHashMap<>(); - - // Standard profile assignments - List> explicitProfiles = getExplicitUserProfiles(securityDb, appId, userId); - boolean hasExplicit = !explicitProfiles.isEmpty(); - - for (Map p : explicitProfiles) { - boolean isGroup = (Boolean) p.getOrDefault("isGroup", Boolean.FALSE); - if (!isGroup) { - Map entry = new LinkedHashMap<>(); - entry.put("profileId", p.get("profileId")); - entry.put("profileName", p.get("profileName")); - entry.put("isDefault", false); - directProfiles.add(entry); - } - } - - // Fall back to default standard profile if no explicit assignment - if (!hasExplicit) { - Map defaultProfile = getDefaultProfile(securityDb, appId); - if (defaultProfile != null && !(Boolean) defaultProfile.getOrDefault("isGroup", Boolean.FALSE)) { - Map entry = new LinkedHashMap<>(); - entry.put("profileId", defaultProfile.get("profileId")); - entry.put("profileName", defaultProfile.get("profileName")); - entry.put("isDefault", true); - directProfiles.add(entry); - } - } - - // Subgroup memberships - List> subgroups = getExplicitUserSubgroups(securityDb, appId, userId); - for (Map sg : subgroups) { - String parentProfileName = (String) sg.get("profileName"); - String subgroupName = (String) sg.get("subgroupName"); - groupMemberships.computeIfAbsent(parentProfileName, k -> new ArrayList<>()).add(subgroupName); - } - - result.put("profiles", directProfiles); - result.put("groups", groupMemberships); - return result; - } - /** * Returns all users assigned to a profile, including display name and email via * JOIN with SMSS_USER. @@ -762,349 +652,6 @@ public static List> getProfileUsers(String appId, String pro return QueryExecutionUtility.flushRsToMap(securityDb, qs); } - // ─── Subgroup CRUD ─────────────────────────────────────────────────────── - - /** - * Creates a new named sub-group within a group-style profile and returns its - * metadata map. - */ - public static Map createSubgroup(String appId, String profileId, String name, - String description, User user) { - if (name == null || name.trim().isEmpty()) { - throw new IllegalArgumentException("Subgroup name cannot be blank."); - } - if (name.trim().length() > 100) { - throw new IllegalArgumentException("Subgroup name cannot exceed 100 characters."); - } - // Verify parent profile is group-style - if (!isGroupProfile(appId, profileId)) { - throw new IllegalArgumentException("Sub-groups can only be added to group-style profiles."); - } - String subgroupId = UUID.randomUUID().toString(); - String actorId = getUserId(user); - IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - PreparedStatement ps = null; - try { - ps = securityDb.getPreparedStatement( - "INSERT INTO APP_PROFILE_SUBGROUP (SUBGROUP_ID, PROFILE_ID, APP_ID, SUBGROUP_NAME, DESCRIPTION, CREATED_BY, CREATED_AT) VALUES (?,?,?,?,?,?,?)"); - int i = 1; - ps.setString(i++, subgroupId); - ps.setString(i++, profileId); - ps.setString(i++, appId); - ps.setString(i++, name.trim()); - ps.setString(i++, description); - ps.setString(i++, actorId); - ps.setTimestamp(i++, Utility.getCurrentSqlTimestampUTC()); - ps.execute(); - if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); - } catch (SQLException e) { - classLogger.error("Failed to create subgroup", e); - throw new IllegalArgumentException("An error occurred creating the subgroup."); - } finally { - ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); - } - Map result = new HashMap<>(); - result.put("subgroupId", subgroupId); - result.put("profileId", profileId); - result.put("subgroupName", name.trim()); - result.put("description", description); - return result; - } - - /** - * Updates the name and/or description of an existing sub-group. Null parameters - * are ignored. - */ - public static void updateSubgroup(String appId, String subgroupId, String name, - String description, User user) { - IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - StringBuilder sb = new StringBuilder("UPDATE APP_PROFILE_SUBGROUP SET"); - List params = new ArrayList<>(); - if (name != null) { - if (name.trim().isEmpty()) throw new IllegalArgumentException("Subgroup name cannot be blank."); - sb.append(" SUBGROUP_NAME=?,"); params.add(name.trim()); - } - if (description != null) { sb.append(" DESCRIPTION=?,"); params.add(description); } - if (params.isEmpty()) return; - sb.setLength(sb.length() - 1); - sb.append(" WHERE SUBGROUP_ID=? AND APP_ID=?"); - params.add(subgroupId); - params.add(appId); - PreparedStatement ps = null; - try { - ps = securityDb.getPreparedStatement(sb.toString()); - for (int i = 0; i < params.size(); i++) { - ps.setString(i + 1, params.get(i)); - } - ps.execute(); - if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); - } catch (SQLException e) { - classLogger.error("Failed to update subgroup", e); - throw new IllegalArgumentException("An error occurred updating the subgroup."); - } finally { - ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); - } - } - - /** - * Deletes a sub-group and its user and feature assignments. - */ - public static void deleteSubgroup(String appId, String subgroupId, User user) { - IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - deleteSubgroupInternal(securityDb, appId, subgroupId); - PreparedStatement ps = null; - try { - ps = securityDb.getPreparedStatement("DELETE FROM APP_PROFILE_SUBGROUP WHERE SUBGROUP_ID=? AND APP_ID=?"); - ps.setString(1, subgroupId); - ps.setString(2, appId); - ps.execute(); - if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); - } catch (SQLException e) { - classLogger.error("Failed to delete subgroup", e); - throw new IllegalArgumentException("An error occurred deleting the subgroup."); - } finally { - ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); - } - } - - /** - * Returns all sub-groups for a profile, each with a live USER_COUNT from - * APP_USER_SUBGROUP via LEFT JOIN + GROUP BY. - */ - public static List> getSubgroups(String appId, String profileId) { - IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - SelectQueryStruct qs = new SelectQueryStruct(); - qs.addSelector(new QueryColumnSelector("APP_PROFILE_SUBGROUP__SUBGROUP_ID", "subgroupId")); - qs.addSelector(new QueryColumnSelector("APP_PROFILE_SUBGROUP__SUBGROUP_NAME", "subgroupName")); - qs.addSelector(new QueryColumnSelector("APP_PROFILE_SUBGROUP__DESCRIPTION", "description")); - qs.addSelector(new QueryColumnSelector("APP_PROFILE_SUBGROUP__CREATED_BY", "createdBy")); - qs.addSelector(new QueryColumnSelector("APP_PROFILE_SUBGROUP__CREATED_AT", "createdAt")); - qs.addSelector(QueryFunctionSelector.makeFunctionSelector(QueryFunctionHelper.COUNT, "APP_USER_SUBGROUP__USER_ID", "userCount")); - qs.addRelation("APP_PROFILE_SUBGROUP__SUBGROUP_ID", "APP_USER_SUBGROUP__SUBGROUP_ID", "left.join"); - qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE_SUBGROUP__APP_ID", "==", appId)); - qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE_SUBGROUP__PROFILE_ID", "==", profileId)); - qs.addGroupBy(new QueryColumnSelector("APP_PROFILE_SUBGROUP__SUBGROUP_ID")); - qs.addGroupBy(new QueryColumnSelector("APP_PROFILE_SUBGROUP__SUBGROUP_NAME")); - qs.addGroupBy(new QueryColumnSelector("APP_PROFILE_SUBGROUP__DESCRIPTION")); - qs.addGroupBy(new QueryColumnSelector("APP_PROFILE_SUBGROUP__CREATED_BY")); - qs.addGroupBy(new QueryColumnSelector("APP_PROFILE_SUBGROUP__CREATED_AT")); - qs.addOrderBy(new QueryColumnOrderBySelector("APP_PROFILE_SUBGROUP__SUBGROUP_NAME")); - return QueryExecutionUtility.flushRsToMap(securityDb, qs); - } - - /** - * Returns all users assigned to a sub-group, including display name and email - * via JOIN with SMSS_USER. - */ - public static List> getSubgroupUsers(String appId, String subgroupId) { - IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - SelectQueryStruct qs = new SelectQueryStruct(); - qs.addSelector(new QueryColumnSelector("APP_USER_SUBGROUP__USER_ID", "userId")); - qs.addSelector(new QueryColumnSelector("SMSS_USER__NAME", "name")); - qs.addSelector(new QueryColumnSelector("SMSS_USER__EMAIL", "email")); - qs.addSelector(new QueryColumnSelector("APP_USER_SUBGROUP__ASSIGNED_BY", "assignedBy")); - qs.addSelector(new QueryColumnSelector("APP_USER_SUBGROUP__ASSIGNED_AT", "assignedAt")); - qs.addRelation("APP_USER_SUBGROUP__USER_ID", "SMSS_USER__ID", "left.join"); - qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_SUBGROUP__APP_ID", "==", appId)); - qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_SUBGROUP__SUBGROUP_ID", "==", subgroupId)); - return QueryExecutionUtility.flushRsToMap(securityDb, qs); - } - - // ─── Subgroup-Feature Assignment ──────────────────────────────────────── - - /** - * Sets the enabled state of a feature for a sub-group (upsert via - * delete+insert). - */ - public static void setSubgroupFeature(String appId, String subgroupId, String featureId, - boolean enabled, User user) { - IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - PreparedStatement ps = null; - try { - ps = securityDb.getPreparedStatement( - "DELETE FROM APP_SUBGROUP_FEATURE WHERE APP_ID=? AND SUBGROUP_ID=? AND FEATURE_ID=?"); - ps.setString(1, appId); - ps.setString(2, subgroupId); - ps.setString(3, featureId); - ps.execute(); - if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); - } catch (SQLException e) { - classLogger.error("Failed to delete existing subgroup feature row", e); - } finally { - ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); - } - ps = null; - try { - ps = securityDb.getPreparedStatement( - "INSERT INTO APP_SUBGROUP_FEATURE (APP_ID, SUBGROUP_ID, FEATURE_ID, ENABLED) VALUES (?,?,?,?)"); - ps.setString(1, appId); - ps.setString(2, subgroupId); - ps.setString(3, featureId); - ps.setBoolean(4, enabled); - ps.execute(); - if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); - } catch (SQLException e) { - classLogger.error("Failed to insert subgroup feature", e); - throw new IllegalArgumentException("An error occurred setting the subgroup feature."); - } finally { - ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); - } - } - - /** - * Returns all features for an app merged with their enabled state for the given - * sub-group. Defaults to false for features without an explicit assignment. - */ - public static List> getSubgroupFeatures(String appId, String subgroupId) { - IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - SelectQueryStruct qs1 = new SelectQueryStruct(); - qs1.addSelector(new QueryColumnSelector("APP_SUBGROUP_FEATURE__FEATURE_ID", "featureId")); - qs1.addSelector(new QueryColumnSelector("APP_SUBGROUP_FEATURE__ENABLED", "enabled")); - qs1.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_SUBGROUP_FEATURE__APP_ID", "==", appId)); - qs1.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_SUBGROUP_FEATURE__SUBGROUP_ID", "==", subgroupId)); - Map enabledMap = new HashMap<>(); - for (Map r : QueryExecutionUtility.flushRsToMap(securityDb, qs1)) { - enabledMap.put((String) r.get("featureId"), Boolean.TRUE.equals(r.get("enabled"))); - } - SelectQueryStruct qs2 = new SelectQueryStruct(); - qs2.addSelector(new QueryColumnSelector("APP_FEATURE__FEATURE_ID", "featureId")); - qs2.addSelector(new QueryColumnSelector("APP_FEATURE__FEATURE_KEY", "featureKey")); - qs2.addSelector(new QueryColumnSelector("APP_FEATURE__DESCRIPTION", "description")); - qs2.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_FEATURE__APP_ID", "==", appId)); - qs2.addOrderBy(new QueryColumnOrderBySelector("APP_FEATURE__FEATURE_KEY")); - List> results = QueryExecutionUtility.flushRsToMap(securityDb, qs2); - results.forEach(r -> r.put("enabled", enabledMap.getOrDefault((String) r.get("featureId"), Boolean.FALSE))); - return results; - } - - // ─── User-Subgroup Assignment ──────────────────────────────────────────── - - /** - * Assigns a user to a sub-group. If already assigned, this is a no-op. - */ - public static void assignUserSubgroup(String appId, String userId, String subgroupId, User actor) { - IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - // check for existing assignment - SelectQueryStruct checkQs = new SelectQueryStruct(); - QueryFunctionSelector countFn = new QueryFunctionSelector(); - countFn.addInnerSelector(new QueryColumnSelector("APP_USER_SUBGROUP__USER_ID")); - countFn.setFunction(QueryFunctionHelper.COUNT); - checkQs.addSelector(countFn); - checkQs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_SUBGROUP__APP_ID", "==", appId)); - checkQs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_SUBGROUP__USER_ID", "==", userId)); - checkQs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_SUBGROUP__SUBGROUP_ID", "==", subgroupId)); - Integer existingCount = QueryExecutionUtility.flushToInteger(securityDb, checkQs); - if (existingCount != null && existingCount > 0) return; - - String actorId = getUserId(actor); - PreparedStatement ps = null; - try { - ps = securityDb.getPreparedStatement( - "INSERT INTO APP_USER_SUBGROUP (APP_ID, USER_ID, SUBGROUP_ID, ASSIGNED_BY, ASSIGNED_AT) VALUES (?,?,?,?,?)"); - ps.setString(1, appId); - ps.setString(2, userId); - ps.setString(3, subgroupId); - ps.setString(4, actorId); - ps.setTimestamp(5, Utility.getCurrentSqlTimestampUTC()); - ps.execute(); - if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); - } catch (SQLException e) { - classLogger.error("Failed to assign user to subgroup", e); - throw new IllegalArgumentException("An error occurred assigning the user to the subgroup."); - } finally { - ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); - } - } - - /** - * Bulk-assigns a list of users to a sub-group. Each user is processed independently: - * users already in the sub-group are silently skipped, users not found in {@code SMSS_USER} - * are recorded in the errors bucket. - * - * @param appId the app ID - * @param userIds non-empty list of {@code SMSS_USER.ID} values to assign - * @param subgroupId the sub-group to assign users to - * @param actor the user performing the assignment - * @return a map with keys: {@code assigned} ({@code List}), {@code skipped} - * ({@code List}), {@code errors} ({@code Map}) - */ - public static Map assignUsersToSubgroup(String appId, List userIds, String subgroupId, User actor) { - List assigned = new ArrayList<>(); - List skipped = new ArrayList<>(); - Map errors = new LinkedHashMap<>(); - IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - String actorId = getUserId(actor); - - for (String userId : userIds) { - if (!userExists(securityDb, userId)) { - classLogger.warn("assignUsersToSubgroup: user '{}' not found in SMSS_USER — adding to errors", userId); - errors.put(userId, "User not found."); - continue; - } - - SelectQueryStruct checkQs = new SelectQueryStruct(); - QueryFunctionSelector countFn = new QueryFunctionSelector(); - countFn.addInnerSelector(new QueryColumnSelector("APP_USER_SUBGROUP__USER_ID")); - countFn.setFunction(QueryFunctionHelper.COUNT); - checkQs.addSelector(countFn); - checkQs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_SUBGROUP__APP_ID", "==", appId)); - checkQs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_SUBGROUP__USER_ID", "==", userId)); - checkQs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_SUBGROUP__SUBGROUP_ID", "==", subgroupId)); - Integer existingCount = QueryExecutionUtility.flushToInteger(securityDb, checkQs); - if (existingCount != null && existingCount > 0) { - classLogger.debug("assignUsersToSubgroup: user '{}' already in subgroup '{}' for app '{}' — skipping", userId, subgroupId, appId); - skipped.add(userId); - continue; - } - - PreparedStatement ps = null; - try { - ps = securityDb.getPreparedStatement( - "INSERT INTO APP_USER_SUBGROUP (APP_ID, USER_ID, SUBGROUP_ID, ASSIGNED_BY, ASSIGNED_AT) VALUES (?,?,?,?,?)"); - ps.setString(1, appId); - ps.setString(2, userId); - ps.setString(3, subgroupId); - ps.setString(4, actorId); - ps.setTimestamp(5, Utility.getCurrentSqlTimestampUTC()); - ps.execute(); - if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); - assigned.add(userId); - } catch (SQLException e) { - classLogger.error("assignUsersToSubgroup: DB error assigning user '{}'", userId, e); - errors.put(userId, "Database error during assignment."); - } finally { - ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); - } - } - - Map result = new LinkedHashMap<>(); - result.put("assigned", assigned); - result.put("skipped", skipped); - result.put("errors", errors); - return result; - } - - /** - * Removes a user from a specific sub-group assignment. - */ - public static void removeUserSubgroup(String appId, String userId, String subgroupId) { - IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - PreparedStatement ps = null; - try { - ps = securityDb.getPreparedStatement( - "DELETE FROM APP_USER_SUBGROUP WHERE APP_ID=? AND USER_ID=? AND SUBGROUP_ID=?"); - ps.setString(1, appId); - ps.setString(2, userId); - ps.setString(3, subgroupId); - ps.execute(); - if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); - } catch (SQLException e) { - classLogger.error("Failed to remove user from subgroup", e); - } finally { - ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); - } - } - // ─── Profile Manager (delegated BU admin) ──────────────────────────────── /** @@ -1113,7 +660,6 @@ public static void removeUserSubgroup(String appId, String userId, String subgro */ public static void addProfileManager(String appId, String userId, User actor) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - // check for existing SelectQueryStruct checkQs = new SelectQueryStruct(); QueryFunctionSelector countFn = new QueryFunctionSelector(); countFn.addInnerSelector(new QueryColumnSelector("APP_PROFILE_MANAGER__USER_ID")); @@ -1160,8 +706,7 @@ public static void removeProfileManager(String appId, String userId) { } /** - * Returns all users with delegated profile manager permission for an app, with - * display name and email via JOIN with SMSS_USER. + * Returns all users with delegated profile manager permission for an app. */ public static List> getProfileManagers(String appId) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); @@ -1179,8 +724,7 @@ public static List> getProfileManagers(String appId) { /** * Returns true if the given feature key is enabled for the calling user in the - * app, evaluated across all assigned profiles, subgroups, and the default - * profile fallback. + * app, evaluated across all assigned profiles with default profile fallback. */ public static boolean checkFeature(String appId, String featureKey, User user) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); @@ -1188,89 +732,77 @@ public static boolean checkFeature(String appId, String featureKey, User user) { if (featureId == null) return false; String userId = getUserId(user); - // Check across all standard profiles List> profiles = getExplicitUserProfiles(securityDb, appId, userId); if (profiles.isEmpty()) { Map defaultProfile = getDefaultProfile(securityDb, appId); - if (defaultProfile != null && !(Boolean) defaultProfile.getOrDefault("isGroup", Boolean.FALSE)) { - if (queryFeatureEnabled(securityDb, appId, (String) defaultProfile.get("profileId"), featureId)) { - return true; - } - } - } else { - for (Map p : profiles) { - if (!(Boolean) p.getOrDefault("isGroup", Boolean.FALSE)) { - if (queryFeatureEnabled(securityDb, appId, (String) p.get("profileId"), featureId)) { - return true; - } - } + if (defaultProfile != null) { + return queryFeatureEnabled(securityDb, appId, (String) defaultProfile.get("profileId"), featureId); } + return false; } - - // Check across all subgroup memberships, plus the parent group profile's base features - List> subgroups = getExplicitUserSubgroups(securityDb, appId, userId); - for (Map sg : subgroups) { - if (querySubgroupFeatureEnabled(securityDb, appId, (String) sg.get("subgroupId"), featureId)) { - return true; - } - // Group profile base features apply to all subgroup members - String parentProfileId = (String) sg.get("profileId"); - if (parentProfileId != null && queryFeatureEnabled(securityDb, appId, parentProfileId, featureId)) { + for (Map p : profiles) { + if (queryFeatureEnabled(securityDb, appId, (String) p.get("profileId"), featureId)) { return true; } } - return false; } /** - * Returns all enabled features for the calling user, across all profiles and - * subgroup memberships (union). Falls back to the default profile if unassigned. + * Returns a boolean feature map for the calling user — all app features keyed + * by featureKey, true if enabled for this user across any assigned profile. + * Falls back to the default profile if the user has no explicit assignment. */ - public static Map getUserFeatures(String appId, User user) { + public static Map getUserFeatures(String appId, User user) { IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); String userId = getUserId(user); - Map result = new HashMap<>(); + Map catalog = getFeatureCatalog(securityDb, appId); + Set enabledIds = new HashSet<>(); - // Standard profile features List> explicitProfiles = getExplicitUserProfiles(securityDb, appId, userId); if (explicitProfiles.isEmpty()) { Map defaultProfile = getDefaultProfile(securityDb, appId); - if (defaultProfile != null && !(Boolean) defaultProfile.getOrDefault("isGroup", Boolean.FALSE)) { - addProfileFeaturesToResult(securityDb, appId, - (String) defaultProfile.get("profileId"), - (String) defaultProfile.get("profileName"), - true, result); + if (defaultProfile != null) { + enabledIds.addAll(getEnabledProfileFeatureIds(securityDb, appId, (String) defaultProfile.get("profileId"))); } } else { - for (Map p : explicitProfiles) { - if (!(Boolean) p.getOrDefault("isGroup", Boolean.FALSE)) { - addProfileFeaturesToResult(securityDb, appId, - (String) p.get("profileId"), - (String) p.get("profileName"), - false, result); - } - } + explicitProfiles.forEach(p -> enabledIds.addAll(getEnabledProfileFeatureIds(securityDb, appId, (String) p.get("profileId")))); } - // Subgroup features + parent group profile base features - List> subgroups = getExplicitUserSubgroups(securityDb, appId, userId); - for (Map sg : subgroups) { - addSubgroupFeaturesToResult(securityDb, appId, - (String) sg.get("subgroupId"), - (String) sg.get("subgroupName"), - (String) sg.get("profileName"), - result); - // Group profile base features apply to all subgroup members - String parentProfileId = (String) sg.get("profileId"); - if (parentProfileId != null) { - addProfileFeaturesToResult(securityDb, appId, - parentProfileId, - (String) sg.get("profileName"), - false, result); + return buildFeatureMap(catalog, enabledIds); + } + + /** + * Returns the calling user's profile(s) for this app, each with a full feature map. + * Falls back to the default profile if the user has no explicit assignment. + */ + public static List> getUserAppProfiles(String appId, User user) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + String userId = getUserId(user); + Map catalog = getFeatureCatalog(securityDb, appId); + List> result = new ArrayList<>(); + + List> explicitProfiles = getExplicitUserProfiles(securityDb, appId, userId); + if (!explicitProfiles.isEmpty()) { + for (Map p : explicitProfiles) { + Map entry = new LinkedHashMap<>(); + entry.put("profileId", p.get("profileId")); + entry.put("profileName", p.get("profileName")); + entry.put("isDefault", false); + entry.put("features", buildFeatureMap(catalog, getEnabledProfileFeatureIds(securityDb, appId, (String) p.get("profileId")))); + result.add(entry); + } + } else { + Map defaultProfile = getDefaultProfile(securityDb, appId); + if (defaultProfile != null) { + Map entry = new LinkedHashMap<>(); + entry.put("profileId", defaultProfile.get("profileId")); + entry.put("profileName", defaultProfile.get("profileName")); + entry.put("isDefault", true); + entry.put("features", buildFeatureMap(catalog, getEnabledProfileFeatureIds(securityDb, appId, (String) defaultProfile.get("profileId")))); + result.add(entry); } } - return result; } @@ -1316,99 +848,65 @@ private static int getAssignedUserCount(IRDBMSEngine securityDb, String appId, S return count != null ? count : 0; } - private static Map getDefaultProfile(IRDBMSEngine securityDb, String appId) { SelectQueryStruct qs = new SelectQueryStruct(); qs.addSelector(new QueryColumnSelector("APP_PROFILE__PROFILE_ID", "profileId")); qs.addSelector(new QueryColumnSelector("APP_PROFILE__PROFILE_NAME", "profileName")); - qs.addSelector(new QueryColumnSelector("APP_PROFILE__IS_GROUP", "isGroup")); qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE__APP_ID", "==", appId)); qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE__IS_DEFAULT", "==", Boolean.TRUE)); List> rows = QueryExecutionUtility.flushRsToMap(securityDb, qs); - if (rows.isEmpty()) return null; - Map row = rows.get(0); - row.put("isGroup", Boolean.TRUE.equals(row.get("isGroup"))); - row.put("isExplicitAssignment", false); - return row; + return rows.isEmpty() ? null : rows.get(0); } /** - * Returns all explicit profile assignments for a user (not falling back to default). + * Returns all explicit profile assignments for a user. + * Two single-table queries + in-memory join to avoid unreliable OWL join traversal. */ private static List> getExplicitUserProfiles(IRDBMSEngine securityDb, String appId, String userId) { - SelectQueryStruct qs = new SelectQueryStruct(); - qs.addSelector(new QueryColumnSelector("APP_PROFILE__PROFILE_ID", "profileId")); - qs.addSelector(new QueryColumnSelector("APP_PROFILE__PROFILE_NAME", "profileName")); - qs.addSelector(new QueryColumnSelector("APP_PROFILE__IS_GROUP", "isGroup")); - qs.addRelation("APP_USER_PROFILE__PROFILE_ID", "APP_PROFILE__PROFILE_ID", "inner.join"); - qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_PROFILE__APP_ID", "==", appId)); - qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_PROFILE__USER_ID", "==", userId)); - List> profiles = QueryExecutionUtility.flushRsToMap(securityDb, qs); - profiles.forEach(r -> r.put("isGroup", Boolean.TRUE.equals(r.get("isGroup")))); - return profiles; + SelectQueryStruct qs1 = new SelectQueryStruct(); + qs1.addSelector(new QueryColumnSelector("APP_USER_PROFILE__PROFILE_ID", "profileId")); + qs1.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_PROFILE__APP_ID", "==", appId)); + qs1.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_PROFILE__USER_ID", "==", userId)); + Set assignedIds = QueryExecutionUtility.flushRsToMap(securityDb, qs1).stream() + .map(r -> (String) r.get("profileId")).collect(Collectors.toSet()); + if (assignedIds.isEmpty()) return new ArrayList<>(); + + SelectQueryStruct qs2 = new SelectQueryStruct(); + qs2.addSelector(new QueryColumnSelector("APP_PROFILE__PROFILE_ID", "profileId")); + qs2.addSelector(new QueryColumnSelector("APP_PROFILE__PROFILE_NAME", "profileName")); + qs2.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE__APP_ID", "==", appId)); + return QueryExecutionUtility.flushRsToMap(securityDb, qs2).stream() + .filter(p -> assignedIds.contains((String) p.get("profileId"))) + .collect(Collectors.toList()); } - /** - * Returns all subgroup assignments for a user, including parent profile name. - */ - private static List> getExplicitUserSubgroups(IRDBMSEngine securityDb, - String appId, String userId) { + /** Returns featureId → featureKey map for all features in this app. */ + private static Map getFeatureCatalog(IRDBMSEngine securityDb, String appId) { SelectQueryStruct qs = new SelectQueryStruct(); - qs.addSelector(new QueryColumnSelector("APP_USER_SUBGROUP__SUBGROUP_ID", "subgroupId")); - qs.addSelector(new QueryColumnSelector("APP_PROFILE_SUBGROUP__SUBGROUP_NAME", "subgroupName")); - qs.addSelector(new QueryColumnSelector("APP_PROFILE__PROFILE_ID", "profileId")); - qs.addSelector(new QueryColumnSelector("APP_PROFILE__PROFILE_NAME", "profileName")); - qs.addRelation("APP_USER_SUBGROUP__SUBGROUP_ID", "APP_PROFILE_SUBGROUP__SUBGROUP_ID", "inner.join"); - qs.addRelation("APP_PROFILE_SUBGROUP__PROFILE_ID", "APP_PROFILE__PROFILE_ID", "inner.join"); - qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_SUBGROUP__APP_ID", "==", appId)); - qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_USER_SUBGROUP__USER_ID", "==", userId)); - return QueryExecutionUtility.flushRsToMap(securityDb, qs); + qs.addSelector(new QueryColumnSelector("APP_FEATURE__FEATURE_ID", "featureId")); + qs.addSelector(new QueryColumnSelector("APP_FEATURE__FEATURE_KEY", "featureKey")); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_FEATURE__APP_ID", "==", appId)); + return QueryExecutionUtility.flushRsToMap(securityDb, qs).stream() + .collect(Collectors.toMap(r -> (String) r.get("featureId"), r -> (String) r.get("featureKey"))); } - private static void addProfileFeaturesToResult(IRDBMSEngine securityDb, String appId, - String profileId, String profileName, boolean isDefaultProfile, - Map result) { + /** Returns the set of enabled featureIds for a profile. */ + private static Set getEnabledProfileFeatureIds(IRDBMSEngine securityDb, String appId, String profileId) { SelectQueryStruct qs = new SelectQueryStruct(); - qs.addSelector(new QueryColumnSelector("APP_FEATURE__FEATURE_KEY", "featureKey")); - qs.addSelector(new QueryColumnSelector("APP_FEATURE__FEATURE_ID", "featureId")); - qs.addRelation("APP_PROFILE_FEATURE__FEATURE_ID", "APP_FEATURE__FEATURE_ID", "inner.join"); + qs.addSelector(new QueryColumnSelector("APP_PROFILE_FEATURE__FEATURE_ID", "featureId")); qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE_FEATURE__APP_ID", "==", appId)); qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE_FEATURE__PROFILE_ID", "==", profileId)); qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE_FEATURE__ENABLED", "==", Boolean.TRUE)); - for (Map row : QueryExecutionUtility.flushRsToMap(securityDb, qs)) { - String featureKey = (String) row.get("featureKey"); - if (!result.containsKey(featureKey)) { - Map featureInfo = new HashMap<>(); - featureInfo.put("featureId", row.get("featureId")); - featureInfo.put("profileName", profileName); - featureInfo.put("isDefaultProfile", isDefaultProfile); - result.put(featureKey, featureInfo); - } - } + return QueryExecutionUtility.flushRsToMap(securityDb, qs).stream() + .map(r -> (String) r.get("featureId")).collect(Collectors.toSet()); } - private static void addSubgroupFeaturesToResult(IRDBMSEngine securityDb, String appId, - String subgroupId, String subgroupName, String parentProfileName, - Map result) { - SelectQueryStruct qs = new SelectQueryStruct(); - qs.addSelector(new QueryColumnSelector("APP_FEATURE__FEATURE_KEY", "featureKey")); - qs.addSelector(new QueryColumnSelector("APP_FEATURE__FEATURE_ID", "featureId")); - qs.addRelation("APP_SUBGROUP_FEATURE__FEATURE_ID", "APP_FEATURE__FEATURE_ID", "inner.join"); - qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_SUBGROUP_FEATURE__APP_ID", "==", appId)); - qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_SUBGROUP_FEATURE__SUBGROUP_ID", "==", subgroupId)); - qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_SUBGROUP_FEATURE__ENABLED", "==", Boolean.TRUE)); - for (Map row : QueryExecutionUtility.flushRsToMap(securityDb, qs)) { - String featureKey = (String) row.get("featureKey"); - if (!result.containsKey(featureKey)) { - Map featureInfo = new HashMap<>(); - featureInfo.put("featureId", row.get("featureId")); - featureInfo.put("profileName", parentProfileName); - featureInfo.put("subgroupName", subgroupName); - featureInfo.put("isDefaultProfile", false); - result.put(featureKey, featureInfo); - } - } + /** Builds a {featureKey: true/false} map for all features, true if featureId is in enabledIds. */ + private static Map buildFeatureMap(Map catalog, Set enabledIds) { + Map map = new LinkedHashMap<>(); + catalog.forEach((featureId, featureKey) -> map.put(featureKey, enabledIds.contains(featureId))); + return map; } private static String resolveFeatureId(IRDBMSEngine securityDb, String appId, String featureKey) { @@ -1430,66 +928,6 @@ private static boolean queryFeatureEnabled(IRDBMSEngine securityDb, String appId return !rows.isEmpty() && Boolean.TRUE.equals(rows.get(0).get("enabled")); } - private static boolean querySubgroupFeatureEnabled(IRDBMSEngine securityDb, String appId, - String subgroupId, String featureId) { - SelectQueryStruct qs = new SelectQueryStruct(); - qs.addSelector(new QueryColumnSelector("APP_SUBGROUP_FEATURE__ENABLED", "enabled")); - qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_SUBGROUP_FEATURE__APP_ID", "==", appId)); - qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_SUBGROUP_FEATURE__SUBGROUP_ID", "==", subgroupId)); - qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_SUBGROUP_FEATURE__FEATURE_ID", "==", featureId)); - List> rows = QueryExecutionUtility.flushRsToMap(securityDb, qs); - return !rows.isEmpty() && Boolean.TRUE.equals(rows.get(0).get("enabled")); - } - - private static boolean isGroupProfile(String appId, String profileId) { - IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); - SelectQueryStruct qs = new SelectQueryStruct(); - qs.addSelector(new QueryColumnSelector("APP_PROFILE__IS_GROUP", "isGroup")); - qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE__APP_ID", "==", appId)); - qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE__PROFILE_ID", "==", profileId)); - List> rows = QueryExecutionUtility.flushRsToMap(securityDb, qs); - return !rows.isEmpty() && Boolean.TRUE.equals(rows.get(0).get("isGroup")); - } - - private static List getSubgroupIdsForProfile(IRDBMSEngine securityDb, String appId, String profileId) { - SelectQueryStruct qs = new SelectQueryStruct(); - qs.addSelector(new QueryColumnSelector("APP_PROFILE_SUBGROUP__SUBGROUP_ID", "subgroupId")); - qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE_SUBGROUP__APP_ID", "==", appId)); - qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("APP_PROFILE_SUBGROUP__PROFILE_ID", "==", profileId)); - List ids = new ArrayList<>(); - for (Map r : QueryExecutionUtility.flushRsToMap(securityDb, qs)) { - ids.add((String) r.get("subgroupId")); - } - return ids; - } - - private static void deleteSubgroupInternal(IRDBMSEngine securityDb, String appId, String subgroupId) { - PreparedStatement ps = null; - try { - ps = securityDb.getPreparedStatement("DELETE FROM APP_USER_SUBGROUP WHERE APP_ID=? AND SUBGROUP_ID=?"); - ps.setString(1, appId); - ps.setString(2, subgroupId); - ps.execute(); - if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); - } catch (SQLException e) { - classLogger.error("Failed to cascade delete subgroup user assignments", e); - } finally { - ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); - } - ps = null; - try { - ps = securityDb.getPreparedStatement("DELETE FROM APP_SUBGROUP_FEATURE WHERE APP_ID=? AND SUBGROUP_ID=?"); - ps.setString(1, appId); - ps.setString(2, subgroupId); - ps.execute(); - if (!ps.getConnection().getAutoCommit()) ps.getConnection().commit(); - } catch (SQLException e) { - classLogger.error("Failed to cascade delete subgroup feature assignments", e); - } finally { - ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); - } - } - /** Returns the primary user ID from the user's active access token. */ public static String getUserId(User user) { if (user == null) return null; diff --git a/src/prerna/reactor/appprofile/feature/CheckAppFeatureReactor.java b/src/prerna/reactor/appprofile/feature/CheckAppFeatureReactor.java index 7f0d0b42aad..98a3e2148fe 100644 --- a/src/prerna/reactor/appprofile/feature/CheckAppFeatureReactor.java +++ b/src/prerna/reactor/appprofile/feature/CheckAppFeatureReactor.java @@ -59,9 +59,6 @@ public NounMetadata execute() { String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); String featureKey = this.keyValue.get(ReactorKeysEnum.FEATURE_KEY.getKey()); - if (!AppProfileUtils.canEvaluateFeatures(user, appId)) { - throw new IllegalArgumentException("User does not have access to this app."); - } boolean enabled = AppProfileUtils.checkFeature(appId, featureKey, user); return new NounMetadata(enabled, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); } diff --git a/src/prerna/reactor/appprofile/feature/CheckFeatureReactor.java b/src/prerna/reactor/appprofile/feature/CheckFeatureReactor.java index 8dbac444b96..a781a128c75 100644 --- a/src/prerna/reactor/appprofile/feature/CheckFeatureReactor.java +++ b/src/prerna/reactor/appprofile/feature/CheckFeatureReactor.java @@ -63,9 +63,6 @@ public NounMetadata execute() { String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); String featureKey = this.keyValue.get(ReactorKeysEnum.FEATURE_KEY.getKey()); - if (!AppProfileUtils.canEvaluateFeatures(user, appId)) { - throw new IllegalArgumentException("User does not have access to this app."); - } boolean enabled = AppProfileUtils.checkFeature(appId, featureKey, user); return new NounMetadata(enabled, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); } diff --git a/src/prerna/reactor/appprofile/feature/GetAppUserFeaturesReactor.java b/src/prerna/reactor/appprofile/feature/GetAppUserFeaturesReactor.java index 4db7a697619..ede719a72d2 100644 --- a/src/prerna/reactor/appprofile/feature/GetAppUserFeaturesReactor.java +++ b/src/prerna/reactor/appprofile/feature/GetAppUserFeaturesReactor.java @@ -60,10 +60,7 @@ public NounMetadata execute() { User user = this.insight.getUser(); String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); - if (!AppProfileUtils.canEvaluateFeatures(user, appId)) { - throw new IllegalArgumentException("User does not have access to this app."); - } - Map features = AppProfileUtils.getUserFeatures(appId, user); + Map features = AppProfileUtils.getUserFeatures(appId, user); return new NounMetadata(features, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); } diff --git a/src/prerna/reactor/appprofile/profile/CreateAppProfileReactor.java b/src/prerna/reactor/appprofile/profile/CreateAppProfileReactor.java index 60f81dbc782..ac7536035e9 100644 --- a/src/prerna/reactor/appprofile/profile/CreateAppProfileReactor.java +++ b/src/prerna/reactor/appprofile/profile/CreateAppProfileReactor.java @@ -50,8 +50,8 @@ public class CreateAppProfileReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(CreateAppProfileReactor.class); public CreateAppProfileReactor() { - this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.NAME.getKey(), ReactorKeysEnum.DESCRIPTION.getKey(), ReactorKeysEnum.IS_DEFAULT.getKey(), ReactorKeysEnum.IS_GROUP.getKey() }; - this.keyRequired = new int[] { 1, 1, 0, 0, 0 }; + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.NAME.getKey(), ReactorKeysEnum.DESCRIPTION.getKey(), ReactorKeysEnum.IS_DEFAULT.getKey() }; + this.keyRequired = new int[] { 1, 1, 0, 0 }; } @Override @@ -62,12 +62,11 @@ public NounMetadata execute() { String name = this.keyValue.get(ReactorKeysEnum.NAME.getKey()); String description = this.keyValue.get(ReactorKeysEnum.DESCRIPTION.getKey()); boolean isDefault = Boolean.parseBoolean(this.keyValue.get(ReactorKeysEnum.IS_DEFAULT.getKey())); - boolean isGroup = Boolean.parseBoolean(this.keyValue.get(ReactorKeysEnum.IS_GROUP.getKey())); if (!AppProfileUtils.canManageProfiles(user, appId)) { throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); } - Map result = AppProfileUtils.createProfile(appId, name, description, isDefault, isGroup, user); + Map result = AppProfileUtils.createProfile(appId, name, description, isDefault, user); NounMetadata noun = new NounMetadata(result, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Profile created.")); return noun; diff --git a/src/prerna/reactor/appprofile/profile/UpdateAppProfileReactor.java b/src/prerna/reactor/appprofile/profile/UpdateAppProfileReactor.java index 7894b9cf98f..ba4f5a6845e 100644 --- a/src/prerna/reactor/appprofile/profile/UpdateAppProfileReactor.java +++ b/src/prerna/reactor/appprofile/profile/UpdateAppProfileReactor.java @@ -48,8 +48,8 @@ public class UpdateAppProfileReactor extends AbstractReactor { private static final Logger classLogger = LogManager.getLogger(UpdateAppProfileReactor.class); public UpdateAppProfileReactor() { - this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.PROFILE_ID.getKey(), ReactorKeysEnum.NAME.getKey(), ReactorKeysEnum.DESCRIPTION.getKey(), ReactorKeysEnum.IS_DEFAULT.getKey(), ReactorKeysEnum.IS_GROUP.getKey() }; - this.keyRequired = new int[] { 1, 1, 0, 0, 0, 0 }; + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.PROFILE_ID.getKey(), ReactorKeysEnum.NAME.getKey(), ReactorKeysEnum.DESCRIPTION.getKey(), ReactorKeysEnum.IS_DEFAULT.getKey() }; + this.keyRequired = new int[] { 1, 1, 0, 0, 0 }; } @Override @@ -62,13 +62,11 @@ public NounMetadata execute() { String description = this.keyValue.get(ReactorKeysEnum.DESCRIPTION.getKey()); String isDefaultStr = this.keyValue.get(ReactorKeysEnum.IS_DEFAULT.getKey()); Boolean isDefault = isDefaultStr != null ? Boolean.parseBoolean(isDefaultStr) : null; - String isGroupStr = this.keyValue.get(ReactorKeysEnum.IS_GROUP.getKey()); - Boolean isGroup = isGroupStr != null ? Boolean.parseBoolean(isGroupStr) : null; if (!AppProfileUtils.canManageProfiles(user, appId)) { throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); } - AppProfileUtils.updateProfile(appId, profileId, name, description, isDefault, isGroup, user); + AppProfileUtils.updateProfile(appId, profileId, name, description, isDefault, user); NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Profile updated.")); return noun; diff --git a/src/prerna/reactor/appprofile/subgroup/AssignAppUserSubgroupReactor.java b/src/prerna/reactor/appprofile/subgroup/AssignAppUserSubgroupReactor.java deleted file mode 100644 index 20d2cf3da40..00000000000 --- a/src/prerna/reactor/appprofile/subgroup/AssignAppUserSubgroupReactor.java +++ /dev/null @@ -1,113 +0,0 @@ -/******************************************************************************* - * Copyright 2015 Defense Health Agency (DHA) - * - * If your use of this software does not include any GPLv2 components: - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ---------------------------------------------------------------------------- - * If your use of this software includes any GPLv2 components: - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - *******************************************************************************/ -package prerna.reactor.appprofile.subgroup; - -import java.util.List; -import java.util.Map; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import prerna.auth.User; -import prerna.reactor.AbstractReactor; -import prerna.reactor.appprofile.AppProfileUtils; -import prerna.sablecc2.om.GenRowStruct; -import prerna.sablecc2.om.PixelDataType; -import prerna.sablecc2.om.PixelOperationType; -import prerna.sablecc2.om.ReactorKeysEnum; -import prerna.sablecc2.om.nounmeta.NounMetadata; - -/** - * Bulk-assigns one or more users to a sub-group. Users already in the sub-group are - * silently skipped; unknown users are returned in the {@code errors} bucket. - * - *

Pixel: {@code AssignAppUserSubgroup(app=["appId"], userId=["user1", "user2"], subgroup=["subgroupId"]);}

- * - *

Returns a map with three keys: {@code assigned}, {@code skipped}, {@code errors}.

- */ -public class AssignAppUserSubgroupReactor extends AbstractReactor { - - private static final Logger classLogger = LogManager.getLogger(AssignAppUserSubgroupReactor.class); - - public AssignAppUserSubgroupReactor() { - this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.USER_ID.getKey(), ReactorKeysEnum.SUBGROUP_ID.getKey() }; - this.keyRequired = new int[] { 1, 1, 1 }; - } - - @Override - public NounMetadata execute() { - organizeKeys(); - User user = this.insight.getUser(); - String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); - String subgroupId = this.keyValue.get(ReactorKeysEnum.SUBGROUP_ID.getKey()); - List userIds = getUserIds(); - - if (!AppProfileUtils.canAssignProfiles(user, appId)) { - throw new IllegalArgumentException("User does not have permission to assign users for this app."); - } - - Map result = AppProfileUtils.assignUsersToSubgroup(appId, userIds, subgroupId, user); - classLogger.debug("AssignAppUserSubgroup: assigned={}, skipped={}, errors={}", - ((List) result.get("assigned")).size(), - ((List) result.get("skipped")).size(), - ((Map) result.get("errors")).size()); - - NounMetadata noun = new NounMetadata(result, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); - noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage( - "Bulk assign complete: " + ((List) result.get("assigned")).size() + " assigned, " - + ((List) result.get("skipped")).size() + " skipped, " - + ((Map) result.get("errors")).size() + " errors.")); - return noun; - } - - /** - * Reads the {@code userId} parameter as a list. Validates that the list is non-empty - * and that every item is a non-blank string. - */ - private List getUserIds() { - GenRowStruct grs = this.store.getGenRowStruct(ReactorKeysEnum.USER_ID.getKey()); - if (grs == null || grs.isEmpty()) { - throw new IllegalArgumentException("userId must be provided and must contain at least one value."); - } - List userIds = grs.getAllStrValues(); - if (userIds == null || userIds.isEmpty()) { - throw new IllegalArgumentException("userId must contain at least one value."); - } - for (String id : userIds) { - if (id == null || id.trim().isEmpty()) { - throw new IllegalArgumentException("userId list contains a blank entry — all userId values must be non-blank."); - } - } - return userIds; - } - - @Override - public String getReactorDescription() { - return "Bulk-assign one or more users to a sub-group. Returns assigned, skipped, and errors buckets."; - } -} diff --git a/src/prerna/reactor/appprofile/subgroup/CreateAppSubgroupReactor.java b/src/prerna/reactor/appprofile/subgroup/CreateAppSubgroupReactor.java deleted file mode 100644 index 3f7f71c2383..00000000000 --- a/src/prerna/reactor/appprofile/subgroup/CreateAppSubgroupReactor.java +++ /dev/null @@ -1,79 +0,0 @@ -/******************************************************************************* - * Copyright 2015 Defense Health Agency (DHA) - * - * If your use of this software does not include any GPLv2 components: - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ---------------------------------------------------------------------------- - * If your use of this software includes any GPLv2 components: - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - *******************************************************************************/ -package prerna.reactor.appprofile.subgroup; - -import prerna.reactor.appprofile.AppProfileUtils; - -import java.util.Map; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import prerna.auth.User; -import prerna.reactor.AbstractReactor; -import prerna.sablecc2.om.PixelDataType; -import prerna.sablecc2.om.PixelOperationType; -import prerna.sablecc2.om.ReactorKeysEnum; -import prerna.sablecc2.om.nounmeta.NounMetadata; - -/** - * Create a named sub-group within a group-style profile. - * - *

Pixel: {@code CreateAppSubgroup(app=["appId"], profile=["profileId"], name=["subgroupName"]);}

- */ -public class CreateAppSubgroupReactor extends AbstractReactor { - - private static final Logger classLogger = LogManager.getLogger(CreateAppSubgroupReactor.class); - - public CreateAppSubgroupReactor() { - this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.PROFILE_ID.getKey(), ReactorKeysEnum.NAME.getKey(), ReactorKeysEnum.DESCRIPTION.getKey() }; - this.keyRequired = new int[] { 1, 1, 1, 0 }; - } - - @Override - public NounMetadata execute() { - organizeKeys(); - User user = this.insight.getUser(); - String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); - String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); - String name = this.keyValue.get(ReactorKeysEnum.NAME.getKey()); - String description = this.keyValue.get(ReactorKeysEnum.DESCRIPTION.getKey()); - - if (!AppProfileUtils.canManageProfiles(user, appId)) { - throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); - } - Map result = AppProfileUtils.createSubgroup(appId, profileId, name, description, user); - NounMetadata noun = new NounMetadata(result, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); - noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Subgroup created.")); - return noun; - } - - @Override - public String getReactorDescription() { - return "Create a named sub-group within a group-style profile."; - } -} diff --git a/src/prerna/reactor/appprofile/subgroup/DeleteAppSubgroupReactor.java b/src/prerna/reactor/appprofile/subgroup/DeleteAppSubgroupReactor.java deleted file mode 100644 index 078ac3dc444..00000000000 --- a/src/prerna/reactor/appprofile/subgroup/DeleteAppSubgroupReactor.java +++ /dev/null @@ -1,75 +0,0 @@ -/******************************************************************************* - * Copyright 2015 Defense Health Agency (DHA) - * - * If your use of this software does not include any GPLv2 components: - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ---------------------------------------------------------------------------- - * If your use of this software includes any GPLv2 components: - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - *******************************************************************************/ -package prerna.reactor.appprofile.subgroup; - -import prerna.reactor.appprofile.AppProfileUtils; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import prerna.auth.User; -import prerna.reactor.AbstractReactor; -import prerna.sablecc2.om.PixelDataType; -import prerna.sablecc2.om.PixelOperationType; -import prerna.sablecc2.om.ReactorKeysEnum; -import prerna.sablecc2.om.nounmeta.NounMetadata; - -/** - * Delete a sub-group and its user/feature assignments. - * - *

Pixel: {@code DeleteAppSubgroup(app=["appId"], subgroup=["subgroupId"]);}

- */ -public class DeleteAppSubgroupReactor extends AbstractReactor { - - private static final Logger classLogger = LogManager.getLogger(DeleteAppSubgroupReactor.class); - - public DeleteAppSubgroupReactor() { - this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.SUBGROUP_ID.getKey() }; - this.keyRequired = new int[] { 1, 1 }; - } - - @Override - public NounMetadata execute() { - organizeKeys(); - User user = this.insight.getUser(); - String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); - String subgroupId = this.keyValue.get(ReactorKeysEnum.SUBGROUP_ID.getKey()); - - if (!AppProfileUtils.canManageProfiles(user, appId)) { - throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); - } - AppProfileUtils.deleteSubgroup(appId, subgroupId, user); - NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); - noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Subgroup deleted.")); - return noun; - } - - @Override - public String getReactorDescription() { - return "Delete a sub-group and its user/feature assignments."; - } -} diff --git a/src/prerna/reactor/appprofile/subgroup/GetAppSubgroupFeaturesReactor.java b/src/prerna/reactor/appprofile/subgroup/GetAppSubgroupFeaturesReactor.java deleted file mode 100644 index 3982e5aeaff..00000000000 --- a/src/prerna/reactor/appprofile/subgroup/GetAppSubgroupFeaturesReactor.java +++ /dev/null @@ -1,77 +0,0 @@ -/******************************************************************************* - * Copyright 2015 Defense Health Agency (DHA) - * - * If your use of this software does not include any GPLv2 components: - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ---------------------------------------------------------------------------- - * If your use of this software includes any GPLv2 components: - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - *******************************************************************************/ -package prerna.reactor.appprofile.subgroup; - -import prerna.reactor.appprofile.AppProfileUtils; - -import java.util.List; -import java.util.Map; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import prerna.sablecc2.om.PixelOperationType; -import prerna.sablecc2.om.ReactorKeysEnum; - -import prerna.auth.User; -import prerna.reactor.AbstractReactor; -import prerna.sablecc2.om.PixelDataType; -import prerna.sablecc2.om.nounmeta.NounMetadata; - -/** - * Get all feature flags for a sub-group. - * - *

Pixel: {@code GetAppSubgroupFeatures(app=["appId"], subgroup=["subgroupId"]);}

- */ -public class GetAppSubgroupFeaturesReactor extends AbstractReactor { - - private static final Logger classLogger = LogManager.getLogger(GetAppSubgroupFeaturesReactor.class); - - public GetAppSubgroupFeaturesReactor() { - this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.SUBGROUP_ID.getKey() }; - this.keyRequired = new int[] { 1, 1 }; - } - - @Override - public NounMetadata execute() { - organizeKeys(); - User user = this.insight.getUser(); - String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); - String subgroupId = this.keyValue.get(ReactorKeysEnum.SUBGROUP_ID.getKey()); - - if (!AppProfileUtils.canAssignProfiles(user, appId)) { - throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); - } - List> result = AppProfileUtils.getSubgroupFeatures(appId, subgroupId); - return new NounMetadata(result, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); - } - - @Override - public String getReactorDescription() { - return "Get all feature flags for a sub-group."; - } -} diff --git a/src/prerna/reactor/appprofile/subgroup/GetAppSubgroupUsersReactor.java b/src/prerna/reactor/appprofile/subgroup/GetAppSubgroupUsersReactor.java deleted file mode 100644 index 7cd816fad27..00000000000 --- a/src/prerna/reactor/appprofile/subgroup/GetAppSubgroupUsersReactor.java +++ /dev/null @@ -1,77 +0,0 @@ -/******************************************************************************* - * Copyright 2015 Defense Health Agency (DHA) - * - * If your use of this software does not include any GPLv2 components: - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ---------------------------------------------------------------------------- - * If your use of this software includes any GPLv2 components: - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - *******************************************************************************/ -package prerna.reactor.appprofile.subgroup; - -import prerna.reactor.appprofile.AppProfileUtils; - -import java.util.List; -import java.util.Map; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import prerna.sablecc2.om.PixelOperationType; -import prerna.sablecc2.om.ReactorKeysEnum; - -import prerna.auth.User; -import prerna.reactor.AbstractReactor; -import prerna.sablecc2.om.PixelDataType; -import prerna.sablecc2.om.nounmeta.NounMetadata; - -/** - * Get all users assigned to a sub-group. - * - *

Pixel: {@code GetAppSubgroupUsers(app=["appId"], subgroup=["subgroupId"]);}

- */ -public class GetAppSubgroupUsersReactor extends AbstractReactor { - - private static final Logger classLogger = LogManager.getLogger(GetAppSubgroupUsersReactor.class); - - public GetAppSubgroupUsersReactor() { - this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.SUBGROUP_ID.getKey() }; - this.keyRequired = new int[] { 1, 1 }; - } - - @Override - public NounMetadata execute() { - organizeKeys(); - User user = this.insight.getUser(); - String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); - String subgroupId = this.keyValue.get(ReactorKeysEnum.SUBGROUP_ID.getKey()); - - if (!AppProfileUtils.canAssignProfiles(user, appId)) { - throw new IllegalArgumentException("User does not have permission to view subgroup users for this app."); - } - List> result = AppProfileUtils.getSubgroupUsers(appId, subgroupId); - return new NounMetadata(result, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); - } - - @Override - public String getReactorDescription() { - return "Get all users assigned to a sub-group."; - } -} diff --git a/src/prerna/reactor/appprofile/subgroup/GetAppSubgroupsReactor.java b/src/prerna/reactor/appprofile/subgroup/GetAppSubgroupsReactor.java deleted file mode 100644 index 1829595d266..00000000000 --- a/src/prerna/reactor/appprofile/subgroup/GetAppSubgroupsReactor.java +++ /dev/null @@ -1,76 +0,0 @@ -/******************************************************************************* - * Copyright 2015 Defense Health Agency (DHA) - * - * If your use of this software does not include any GPLv2 components: - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ---------------------------------------------------------------------------- - * If your use of this software includes any GPLv2 components: - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - *******************************************************************************/ -package prerna.reactor.appprofile.subgroup; - -import prerna.reactor.appprofile.AppProfileUtils; - -import java.util.List; -import java.util.Map; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import prerna.auth.User; -import prerna.reactor.AbstractReactor; -import prerna.sablecc2.om.PixelDataType; -import prerna.sablecc2.om.PixelOperationType; -import prerna.sablecc2.om.ReactorKeysEnum; -import prerna.sablecc2.om.nounmeta.NounMetadata; - -/** - * Get all sub-groups within a group-style profile. - * - *

Pixel: {@code GetAppSubgroups(app=["appId"], profile=["profileId"]);}

- */ -public class GetAppSubgroupsReactor extends AbstractReactor { - - private static final Logger classLogger = LogManager.getLogger(GetAppSubgroupsReactor.class); - - public GetAppSubgroupsReactor() { - this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.PROFILE_ID.getKey() }; - this.keyRequired = new int[] { 1, 1 }; - } - - @Override - public NounMetadata execute() { - organizeKeys(); - User user = this.insight.getUser(); - String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); - String profileId = this.keyValue.get(ReactorKeysEnum.PROFILE_ID.getKey()); - - if (!AppProfileUtils.canAssignProfiles(user, appId)) { - throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); - } - List> result = AppProfileUtils.getSubgroups(appId, profileId); - return new NounMetadata(result, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); - } - - @Override - public String getReactorDescription() { - return "Get all sub-groups within a group-style profile."; - } -} diff --git a/src/prerna/reactor/appprofile/subgroup/RemoveAppUserSubgroupReactor.java b/src/prerna/reactor/appprofile/subgroup/RemoveAppUserSubgroupReactor.java deleted file mode 100644 index 240e0b17382..00000000000 --- a/src/prerna/reactor/appprofile/subgroup/RemoveAppUserSubgroupReactor.java +++ /dev/null @@ -1,76 +0,0 @@ -/******************************************************************************* - * Copyright 2015 Defense Health Agency (DHA) - * - * If your use of this software does not include any GPLv2 components: - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ---------------------------------------------------------------------------- - * If your use of this software includes any GPLv2 components: - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - *******************************************************************************/ -package prerna.reactor.appprofile.subgroup; - -import prerna.reactor.appprofile.AppProfileUtils; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import prerna.auth.User; -import prerna.reactor.AbstractReactor; -import prerna.sablecc2.om.PixelDataType; -import prerna.sablecc2.om.PixelOperationType; -import prerna.sablecc2.om.ReactorKeysEnum; -import prerna.sablecc2.om.nounmeta.NounMetadata; - -/** - * Remove a user from a sub-group. - * - *

Pixel: {@code RemoveAppUserSubgroup(app=["appId"], userId=["userId"], subgroup=["subgroupId"]);}

- */ -public class RemoveAppUserSubgroupReactor extends AbstractReactor { - - private static final Logger classLogger = LogManager.getLogger(RemoveAppUserSubgroupReactor.class); - - public RemoveAppUserSubgroupReactor() { - this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.USER_ID.getKey(), ReactorKeysEnum.SUBGROUP_ID.getKey() }; - this.keyRequired = new int[] { 1, 1, 1 }; - } - - @Override - public NounMetadata execute() { - organizeKeys(); - User user = this.insight.getUser(); - String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); - String userId = this.keyValue.get(ReactorKeysEnum.USER_ID.getKey()); - String subgroupId = this.keyValue.get(ReactorKeysEnum.SUBGROUP_ID.getKey()); - - if (!AppProfileUtils.canAssignProfiles(user, appId)) { - throw new IllegalArgumentException("User does not have permission to manage users for this app."); - } - AppProfileUtils.removeUserSubgroup(appId, userId, subgroupId); - NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); - noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("User removed from subgroup.")); - return noun; - } - - @Override - public String getReactorDescription() { - return "Remove a user from a sub-group."; - } -} diff --git a/src/prerna/reactor/appprofile/subgroup/SetAppSubgroupFeatureReactor.java b/src/prerna/reactor/appprofile/subgroup/SetAppSubgroupFeatureReactor.java deleted file mode 100644 index a37b774b1cb..00000000000 --- a/src/prerna/reactor/appprofile/subgroup/SetAppSubgroupFeatureReactor.java +++ /dev/null @@ -1,78 +0,0 @@ -/******************************************************************************* - * Copyright 2015 Defense Health Agency (DHA) - * - * If your use of this software does not include any GPLv2 components: - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ---------------------------------------------------------------------------- - * If your use of this software includes any GPLv2 components: - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - *******************************************************************************/ -package prerna.reactor.appprofile.subgroup; - -import prerna.reactor.appprofile.AppProfileUtils; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import prerna.sablecc2.om.PixelOperationType; -import prerna.sablecc2.om.ReactorKeysEnum; - -import prerna.auth.User; -import prerna.reactor.AbstractReactor; -import prerna.sablecc2.om.PixelDataType; -import prerna.sablecc2.om.nounmeta.NounMetadata; - -/** - * Enable or disable a feature for a sub-group. - * - *

Pixel: {@code SetAppSubgroupFeature(app=["appId"], subgroup=["subgroupId"], feature=["featureId"], enabled=["true"]);}

- */ -public class SetAppSubgroupFeatureReactor extends AbstractReactor { - - private static final Logger classLogger = LogManager.getLogger(SetAppSubgroupFeatureReactor.class); - - public SetAppSubgroupFeatureReactor() { - this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.SUBGROUP_ID.getKey(), ReactorKeysEnum.FEATURE_ID.getKey(), ReactorKeysEnum.ENABLED.getKey() }; - this.keyRequired = new int[] { 1, 1, 1, 1 }; - } - - @Override - public NounMetadata execute() { - organizeKeys(); - User user = this.insight.getUser(); - String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); - String subgroupId = this.keyValue.get(ReactorKeysEnum.SUBGROUP_ID.getKey()); - String featureId = this.keyValue.get(ReactorKeysEnum.FEATURE_ID.getKey()); - boolean enabled = Boolean.parseBoolean(this.keyValue.get(ReactorKeysEnum.ENABLED.getKey())); - - if (!AppProfileUtils.canManageProfiles(user, appId)) { - throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); - } - AppProfileUtils.setSubgroupFeature(appId, subgroupId, featureId, enabled, user); - NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); - noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Subgroup feature updated.")); - return noun; - } - - @Override - public String getReactorDescription() { - return "Enable or disable a feature for a sub-group."; - } -} diff --git a/src/prerna/reactor/appprofile/subgroup/UpdateAppSubgroupReactor.java b/src/prerna/reactor/appprofile/subgroup/UpdateAppSubgroupReactor.java deleted file mode 100644 index 4d1359d5dd2..00000000000 --- a/src/prerna/reactor/appprofile/subgroup/UpdateAppSubgroupReactor.java +++ /dev/null @@ -1,77 +0,0 @@ -/******************************************************************************* - * Copyright 2015 Defense Health Agency (DHA) - * - * If your use of this software does not include any GPLv2 components: - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ---------------------------------------------------------------------------- - * If your use of this software includes any GPLv2 components: - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - *******************************************************************************/ -package prerna.reactor.appprofile.subgroup; - -import prerna.reactor.appprofile.AppProfileUtils; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import prerna.auth.User; -import prerna.reactor.AbstractReactor; -import prerna.sablecc2.om.PixelDataType; -import prerna.sablecc2.om.PixelOperationType; -import prerna.sablecc2.om.ReactorKeysEnum; -import prerna.sablecc2.om.nounmeta.NounMetadata; - -/** - * Update a sub-group's name or description. - * - *

Pixel: {@code UpdateAppSubgroup(app=["appId"], subgroup=["subgroupId"], name=["newName"]);}

- */ -public class UpdateAppSubgroupReactor extends AbstractReactor { - - private static final Logger classLogger = LogManager.getLogger(UpdateAppSubgroupReactor.class); - - public UpdateAppSubgroupReactor() { - this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.SUBGROUP_ID.getKey(), ReactorKeysEnum.NAME.getKey(), ReactorKeysEnum.DESCRIPTION.getKey() }; - this.keyRequired = new int[] { 1, 1, 0, 0 }; - } - - @Override - public NounMetadata execute() { - organizeKeys(); - User user = this.insight.getUser(); - String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); - String subgroupId = this.keyValue.get(ReactorKeysEnum.SUBGROUP_ID.getKey()); - String name = this.keyValue.get(ReactorKeysEnum.NAME.getKey()); - String description = this.keyValue.get(ReactorKeysEnum.DESCRIPTION.getKey()); - - if (!AppProfileUtils.canManageProfiles(user, appId)) { - throw new IllegalArgumentException("User does not have permission to manage profiles for this app."); - } - AppProfileUtils.updateSubgroup(appId, subgroupId, name, description, user); - NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); - noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Subgroup updated.")); - return noun; - } - - @Override - public String getReactorDescription() { - return "Update a sub-group's name or description."; - } -} diff --git a/src/prerna/reactor/appprofile/user/GetUserAppProfilesReactor.java b/src/prerna/reactor/appprofile/user/GetUserAppProfilesReactor.java index cf9362a4eb0..2b9a43fc730 100644 --- a/src/prerna/reactor/appprofile/user/GetUserAppProfilesReactor.java +++ b/src/prerna/reactor/appprofile/user/GetUserAppProfilesReactor.java @@ -29,6 +29,7 @@ import prerna.reactor.appprofile.AppProfileUtils; +import java.util.List; import java.util.Map; import org.apache.logging.log4j.LogManager; @@ -60,10 +61,7 @@ public NounMetadata execute() { User user = this.insight.getUser(); String appId = this.keyValue.get(ReactorKeysEnum.APP.getKey()); - if (!AppProfileUtils.canEvaluateFeatures(user, appId)) { - throw new IllegalArgumentException("User does not have access to this app."); - } - Map result = AppProfileUtils.getUserAppProfiles(appId, user); + List> result = AppProfileUtils.getUserAppProfiles(appId, user); return new NounMetadata(result, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); }