diff --git a/src/prerna/auth/utils/AbstractSecurityUtils.java b/src/prerna/auth/utils/AbstractSecurityUtils.java index 4d2805a7be2..bfccc371c16 100644 --- a/src/prerna/auth/utils/AbstractSecurityUtils.java +++ b/src/prerna/auth/utils/AbstractSecurityUtils.java @@ -2404,6 +2404,307 @@ public static void initialize() throws Exception { } } + // APP_PROFILE — named profiles per app + 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); + 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); + } + } + } + + // 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/SecurityOwlCreator.java b/src/prerna/auth/utils/SecurityOwlCreator.java index 6ce003eb9d7..37e8fb2a53b 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) { @@ -426,6 +433,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, @@ -491,6 +576,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/auth/utils/SecurityProjectUtils.java b/src/prerna/auth/utils/SecurityProjectUtils.java index e1ef865abda..bdf291b0b35 100644 --- a/src/prerna/auth/utils/SecurityProjectUtils.java +++ b/src/prerna/auth/utils/SecurityProjectUtils.java @@ -93,6 +93,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 { @@ -1723,6 +1724,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); } /** @@ -1749,6 +1752,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); } /** @@ -4721,6 +4726,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/AppProfileUtils.java b/src/prerna/reactor/appprofile/AppProfileUtils.java new file mode 100644 index 00000000000..9829a65253c --- /dev/null +++ b/src/prerna/reactor/appprofile/AppProfileUtils.java @@ -0,0 +1,937 @@ +/******************************************************************************* + * 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.sql.PreparedStatement; +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; + +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.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.util.ConnectionUtils; +import prerna.util.QueryExecutionUtility; +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 ────────────────────────────────────────────────── + + /** + * 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)) { + throw new IllegalArgumentException("App not found: " + appId); + } + 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(); + 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; + } + + private static boolean appExists(String appId) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + SelectQueryStruct qs = new SelectQueryStruct(); + qs.addSelector(new QueryColumnSelector("PROJECT__PROJECTID", "projectId")); + qs.addExplicitFilter(SimpleQueryFilter.makeColToValFilter("PROJECT__PROJECTID", "==", appId)); + return QueryExecutionUtility.flushToString(securityDb, qs) != null; + } + + // ─── 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, 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(); + + 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); + } + + PreparedStatement ps = null; + try { + ps = securityDb.getPreparedStatement( + "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++, false); + 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; + } + + /** + * Updates mutable fields on an existing app profile (name, description, + * isDefault). Null parameters are ignored. + */ + 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); + } + } + + /** + * 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(); + 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 {}", profileId, 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 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 via LEFT JOIN + GROUP BY. + */ + public static List> getProfiles(String appId) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + 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__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__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")))); + return profiles; + } + + // ─── 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); + 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; + } + + /** + * 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); + 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); + } + } + + /** + * 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(); + 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); + } + } + + /** + * Returns all feature definitions for an app, ordered by FEATURE_KEY ascending. + */ + public static List> getFeatures(String appId) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + 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 ───────────────────────────────────────── + + /** + * 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(); + 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); + } + } + + /** + * Returns all features for an app merged with their enabled state for the given + * profile. Defaults to false for features without an explicit assignment. + */ + public static List> getProfileFeatures(String appId, String profileId) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + 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<>(); + 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-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 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(); + 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); + 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(); + } 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); + } + } + + /** + * 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. + * + * @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<>(); + 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. + */ + 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 all user profile assignments", e); + } finally { + ConnectionUtils.closeAllConnectionsIfPooling(securityDb, ps); + } + } + + /** + * Removes a user from a specific profile assignment. + */ + public static void removeUserProfile(String appId, String userId, String profileId) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + PreparedStatement ps = null; + try { + 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); + ps.setString(3, profileId); + 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); + } + } + + /** + * Returns all explicit profile assignments for a user in an app. + */ + public static List> getUserProfiles(String appId, String userId) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + return getExplicitUserProfiles(securityDb, appId, userId); + } + + /** + * Returns all users assigned to a profile, including display name and email via + * JOIN with SMSS_USER. + */ + public static List> getProfileUsers(String appId, String profileId) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + 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); + } + + // ─── 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(); + 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; + + PreparedStatement 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); + } + } + + /** + * 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; + 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); + } + } + + /** + * Returns all users with delegated profile manager permission for an app. + */ + public static List> getProfileManagers(String appId) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + 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 ────────────────────────────────────────────────── + + /** + * Returns true if the given feature key is enabled for the calling user in the + * app, evaluated across all assigned profiles with default profile fallback. + */ + 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); + + List> profiles = getExplicitUserProfiles(securityDb, appId, userId); + if (profiles.isEmpty()) { + Map defaultProfile = getDefaultProfile(securityDb, appId); + if (defaultProfile != null) { + return queryFeatureEnabled(securityDb, appId, (String) defaultProfile.get("profileId"), featureId); + } + return false; + } + for (Map p : profiles) { + if (queryFeatureEnabled(securityDb, appId, (String) p.get("profileId"), featureId)) { + return true; + } + } + return false; + } + + /** + * 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) { + IRDBMSEngine securityDb = SystemEngineRegistry.getSecurityDb(); + String userId = getUserId(user); + Map catalog = getFeatureCatalog(securityDb, appId); + Set enabledIds = new HashSet<>(); + + List> explicitProfiles = getExplicitUserProfiles(securityDb, appId, userId); + if (explicitProfiles.isEmpty()) { + Map defaultProfile = getDefaultProfile(securityDb, appId); + if (defaultProfile != null) { + enabledIds.addAll(getEnabledProfileFeatureIds(securityDb, appId, (String) defaultProfile.get("profileId"))); + } + } else { + explicitProfiles.forEach(p -> enabledIds.addAll(getEnabledProfileFeatureIds(securityDb, appId, (String) p.get("profileId")))); + } + + 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; + } + + // ─── 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) { + 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) { + SelectQueryStruct qs = new SelectQueryStruct(); + qs.addSelector(new QueryColumnSelector("APP_PROFILE__PROFILE_ID", "profileId")); + qs.addSelector(new QueryColumnSelector("APP_PROFILE__PROFILE_NAME", "profileName")); + 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); + return rows.isEmpty() ? null : rows.get(0); + } + + /** + * 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 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 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_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"))); + } + + /** 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_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)); + return QueryExecutionUtility.flushRsToMap(securityDb, qs).stream() + .map(r -> (String) r.get("featureId")).collect(Collectors.toSet()); + } + + /** 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) { + 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) { + 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")); + } + + /** 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()); + return token != null ? token.getId() : null; + } +} diff --git a/src/prerna/reactor/appprofile/feature/CheckAppFeatureReactor.java b/src/prerna/reactor/appprofile/feature/CheckAppFeatureReactor.java new file mode 100644 index 00000000000..98a3e2148fe --- /dev/null +++ b/src/prerna/reactor/appprofile/feature/CheckAppFeatureReactor.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.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.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); + + 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()); + + 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/feature/CheckFeatureReactor.java b/src/prerna/reactor/appprofile/feature/CheckFeatureReactor.java new file mode 100644 index 00000000000..a781a128c75 --- /dev/null +++ b/src/prerna/reactor/appprofile/feature/CheckFeatureReactor.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * 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.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.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 +/** + * 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); + + public CheckFeatureReactor() { + 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()); + + 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."; + } +} diff --git a/src/prerna/reactor/appprofile/feature/CreateAppFeatureReactor.java b/src/prerna/reactor/appprofile/feature/CreateAppFeatureReactor.java new file mode 100644 index 00000000000..bb12200be55 --- /dev/null +++ b/src/prerna/reactor/appprofile/feature/CreateAppFeatureReactor.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * 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.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.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); + + public CreateAppFeatureReactor() { + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.FEATURE_KEY.getKey(), ReactorKeysEnum.DESCRIPTION.getKey() }; + this.keyRequired = new int[] { 1, 1, 0 }; + } + + @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()); + 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.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); + 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/feature/DeleteAppFeatureReactor.java b/src/prerna/reactor/appprofile/feature/DeleteAppFeatureReactor.java new file mode 100644 index 00000000000..87af09126a6 --- /dev/null +++ b/src/prerna/reactor/appprofile/feature/DeleteAppFeatureReactor.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * 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.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.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); + + public DeleteAppFeatureReactor() { + this.keysToGet = new String[] { ReactorKeysEnum.APP.getKey(), ReactorKeysEnum.FEATURE_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 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, PixelOperationType.OPERATION); + 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/feature/GetAppFeaturesReactor.java b/src/prerna/reactor/appprofile/feature/GetAppFeaturesReactor.java new file mode 100644 index 00000000000..93831f0b8bf --- /dev/null +++ b/src/prerna/reactor/appprofile/feature/GetAppFeaturesReactor.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * 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.feature; + +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 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); + + public GetAppFeaturesReactor() { + 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.canAssignProfiles(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, PixelOperationType.OPERATION); + } + + @Override + public String getReactorDescription() { + return "Get all feature keys defined for an app."; + } +} diff --git a/src/prerna/reactor/appprofile/feature/GetAppUserFeaturesReactor.java b/src/prerna/reactor/appprofile/feature/GetAppUserFeaturesReactor.java new file mode 100644 index 00000000000..ede719a72d2 --- /dev/null +++ b/src/prerna/reactor/appprofile/feature/GetAppUserFeaturesReactor.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.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.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); + + 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()); + + 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/feature/UpdateAppFeatureReactor.java b/src/prerna/reactor/appprofile/feature/UpdateAppFeatureReactor.java new file mode 100644 index 00000000000..efbf41c1b2b --- /dev/null +++ b/src/prerna/reactor/appprofile/feature/UpdateAppFeatureReactor.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * 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.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.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); + + public UpdateAppFeatureReactor() { + 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 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + 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, PixelOperationType.OPERATION); + 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/manager/AddAppProfileManagerReactor.java b/src/prerna/reactor/appprofile/manager/AddAppProfileManagerReactor.java new file mode 100644 index 00000000000..1231611b29b --- /dev/null +++ b/src/prerna/reactor/appprofile/manager/AddAppProfileManagerReactor.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * 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.manager; + +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; + +/** + * 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); + + 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/manager/GetAppProfileManagersReactor.java b/src/prerna/reactor/appprofile/manager/GetAppProfileManagersReactor.java new file mode 100644 index 00000000000..31fd203df2f --- /dev/null +++ b/src/prerna/reactor/appprofile/manager/GetAppProfileManagersReactor.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * 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.manager; + +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 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); + + 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.canAssignProfiles(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/manager/RemoveAppProfileManagerReactor.java b/src/prerna/reactor/appprofile/manager/RemoveAppProfileManagerReactor.java new file mode 100644 index 00000000000..f59a835036d --- /dev/null +++ b/src/prerna/reactor/appprofile/manager/RemoveAppProfileManagerReactor.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * 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.manager; + +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; + +/** + * 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); + + 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/profile/CreateAppProfileReactor.java b/src/prerna/reactor/appprofile/profile/CreateAppProfileReactor.java new file mode 100644 index 00000000000..ac7536035e9 --- /dev/null +++ b/src/prerna/reactor/appprofile/profile/CreateAppProfileReactor.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * 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.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.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); + + public CreateAppProfileReactor() { + 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 + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + 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())); + + 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.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); + 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/profile/DeleteAppProfileReactor.java b/src/prerna/reactor/appprofile/profile/DeleteAppProfileReactor.java new file mode 100644 index 00000000000..9f290dd5d75 --- /dev/null +++ b/src/prerna/reactor/appprofile/profile/DeleteAppProfileReactor.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * 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.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.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); + + public DeleteAppProfileReactor() { + 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."); + } + AppProfileUtils.deleteProfile(appId, profileId, user); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); + 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/profile/GetAppProfileFeaturesReactor.java b/src/prerna/reactor/appprofile/profile/GetAppProfileFeaturesReactor.java new file mode 100644 index 00000000000..c1c10a78ba3 --- /dev/null +++ b/src/prerna/reactor/appprofile/profile/GetAppProfileFeaturesReactor.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * 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.profile; + +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 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); + + public GetAppProfileFeaturesReactor() { + 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> features = AppProfileUtils.getProfileFeatures(appId, profileId); + return new NounMetadata(features, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); + } + + @Override + public String getReactorDescription() { + return "Get all features with their enabled status for a profile."; + } +} diff --git a/src/prerna/reactor/appprofile/profile/GetAppProfileUsersReactor.java b/src/prerna/reactor/appprofile/profile/GetAppProfileUsersReactor.java new file mode 100644 index 00000000000..62f1b230a6e --- /dev/null +++ b/src/prerna/reactor/appprofile/profile/GetAppProfileUsersReactor.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * 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.profile; + +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 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); + + public GetAppProfileUsersReactor() { + 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> users = AppProfileUtils.getProfileUsers(appId, profileId); + return new NounMetadata(users, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); + } + + @Override + public String getReactorDescription() { + return "Get all users assigned to a profile in an app."; + } +} diff --git a/src/prerna/reactor/appprofile/profile/GetAppProfilesReactor.java b/src/prerna/reactor/appprofile/profile/GetAppProfilesReactor.java new file mode 100644 index 00000000000..430c09b57f0 --- /dev/null +++ b/src/prerna/reactor/appprofile/profile/GetAppProfilesReactor.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * 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.profile; + +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 profiles defined for an app. + * + *

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

+ */ +public class GetAppProfilesReactor extends AbstractReactor { + + private static final Logger classLogger = LogManager.getLogger(GetAppProfilesReactor.class); + + public GetAppProfilesReactor() { + 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.canAssignProfiles(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, PixelOperationType.OPERATION); + } + + @Override + public String getReactorDescription() { + return "Get all profiles defined for an app."; + } +} diff --git a/src/prerna/reactor/appprofile/profile/SetAppProfileFeatureReactor.java b/src/prerna/reactor/appprofile/profile/SetAppProfileFeatureReactor.java new file mode 100644 index 00000000000..39ab41e6b70 --- /dev/null +++ b/src/prerna/reactor/appprofile/profile/SetAppProfileFeatureReactor.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * 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.profile; + +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 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); + + 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 }; + } + + @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 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, PixelOperationType.OPERATION); + 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/profile/UpdateAppProfileReactor.java b/src/prerna/reactor/appprofile/profile/UpdateAppProfileReactor.java new file mode 100644 index 00000000000..ba4f5a6845e --- /dev/null +++ b/src/prerna/reactor/appprofile/profile/UpdateAppProfileReactor.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * 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.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.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); + + public UpdateAppProfileReactor() { + 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 + 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()); + String isDefaultStr = this.keyValue.get(ReactorKeysEnum.IS_DEFAULT.getKey()); + 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, PixelOperationType.OPERATION); + 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/appprofile/user/AssignAppUserProfileReactor.java b/src/prerna/reactor/appprofile/user/AssignAppUserProfileReactor.java new file mode 100644 index 00000000000..bf042d73459 --- /dev/null +++ b/src/prerna/reactor/appprofile/user/AssignAppUserProfileReactor.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * 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.user; + +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 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=["user1", "user2"], profile=["profileId"]);}

+ * + *

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

+ */ +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 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."); + } + + 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 "Bulk-assign one or more users to a profile for an app. Returns assigned, skipped, and errors buckets."; + } +} diff --git a/src/prerna/reactor/appprofile/user/GetUserAppProfilesReactor.java b/src/prerna/reactor/appprofile/user/GetUserAppProfilesReactor.java new file mode 100644 index 00000000000..2b9a43fc730 --- /dev/null +++ b/src/prerna/reactor/appprofile/user/GetUserAppProfilesReactor.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.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.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); + + 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()); + + List> 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/user/GetUserProfileReactor.java b/src/prerna/reactor/appprofile/user/GetUserProfileReactor.java new file mode 100644 index 00000000000..832a5dc9468 --- /dev/null +++ b/src/prerna/reactor/appprofile/user/GetUserProfileReactor.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * 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.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.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); + + public GetUserProfileReactor() { + 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."); + } + List> profiles = AppProfileUtils.getUserProfiles(appId, userId); + return new NounMetadata(profiles, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); + } + + @Override + public String getReactorDescription() { + return "Get all profile assignments for a specific user in an app. Requires manage permission."; + } +} diff --git a/src/prerna/reactor/appprofile/user/RemoveAppUserProfileReactor.java b/src/prerna/reactor/appprofile/user/RemoveAppUserProfileReactor.java new file mode 100644 index 00000000000..ff68e79e0b8 --- /dev/null +++ b/src/prerna/reactor/appprofile/user/RemoveAppUserProfileReactor.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * 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.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.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); + + 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/platformprofile/AssignUserPlatformProfileReactor.java b/src/prerna/reactor/platformprofile/AssignUserPlatformProfileReactor.java new file mode 100644 index 00000000000..9c55382f8b7 --- /dev/null +++ b/src/prerna/reactor/platformprofile/AssignUserPlatformProfileReactor.java @@ -0,0 +1,112 @@ +/******************************************************************************* + * 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 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; + +/** + * 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=[""]);}

+ * + *

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 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + 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()); + 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 "Bulk-assign one or more users to a platform profile. Returns assigned, skipped, and errors buckets."; + } +} diff --git a/src/prerna/reactor/platformprofile/CreatePlatformProfileReactor.java b/src/prerna/reactor/platformprofile/CreatePlatformProfileReactor.java new file mode 100644 index 00000000000..d4dcc5b21d3 --- /dev/null +++ b/src/prerna/reactor/platformprofile/CreatePlatformProfileReactor.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.platformprofile; + +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; +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[] { ReactorKeysEnum.NAME.getKey(), ReactorKeysEnum.DESCRIPTION.getKey() }; + this.keyRequired = new int[] { 1, 0 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + if (!SecurityAdminUtils.userIsAdmin(user)) { + throw new IllegalArgumentException("User must be an admin to manage platform profiles."); + } + 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.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); + 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..cadd2ab2149 --- /dev/null +++ b/src/prerna/reactor/platformprofile/DeletePlatformProfileReactor.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.platformprofile; + +import prerna.auth.User; +import prerna.auth.utils.SecurityAdminUtils; +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[] { ReactorKeysEnum.PROFILE_ID.getKey() }; + this.keyRequired = new int[] { 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + 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()); + PlatformProfileUtils.deleteProfile(profileId, user); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); + 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..cae2c7d1559 --- /dev/null +++ b/src/prerna/reactor/platformprofile/GetPlatformFeaturesReactor.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.platformprofile; + +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; +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[] { ReactorKeysEnum.PROFILE_ID.getKey() }; + this.keyRequired = new int[] { 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + 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()); + Map features = PlatformProfileUtils.getProfileFeatures(profileId); + return new NounMetadata(features, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); + } + + @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/GetPlatformProfileUsersReactor.java b/src/prerna/reactor/platformprofile/GetPlatformProfileUsersReactor.java new file mode 100644 index 00000000000..75a3d8d81aa --- /dev/null +++ b/src/prerna/reactor/platformprofile/GetPlatformProfileUsersReactor.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.platformprofile; + +import java.util.List; +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; +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[] { ReactorKeysEnum.PROFILE_ID.getKey() }; + this.keyRequired = new int[] { 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + 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()); + List> users = PlatformProfileUtils.getPlatformProfileUsers(profileId); + return new NounMetadata(users, PixelDataType.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); + } + + @Override + public String getReactorDescription() { + return "Get all users assigned to a platform profile."; + } +} diff --git a/src/prerna/reactor/platformprofile/GetPlatformProfilesReactor.java b/src/prerna/reactor/platformprofile/GetPlatformProfilesReactor.java new file mode 100644 index 00000000000..2d5f369ce8b --- /dev/null +++ b/src/prerna/reactor/platformprofile/GetPlatformProfilesReactor.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.platformprofile; + +import java.util.List; +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; +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() { + this.keysToGet = new String[] {}; + this.keyRequired = new int[] {}; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + if (!SecurityAdminUtils.userIsAdmin(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, PixelOperationType.OPERATION); + } + + @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..f2554605668 --- /dev/null +++ b/src/prerna/reactor/platformprofile/GetUserPlatformFeaturesReactor.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 java.util.Map; + +import prerna.auth.User; +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() { + 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.CUSTOM_DATA_STRUCTURE, PixelOperationType.OPERATION); + } + + @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/PlatformProfileUtils.java b/src/prerna/reactor/platformprofile/PlatformProfileUtils.java new file mode 100644 index 00000000000..8f565322f56 --- /dev/null +++ b/src/prerna/reactor/platformprofile/PlatformProfileUtils.java @@ -0,0 +1,462 @@ +/******************************************************************************* + * 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.sql.PreparedStatement; +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.Map; +import java.util.Set; +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.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.util.ConnectionUtils; +import prerna.util.QueryExecutionUtility; +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.skills", + "nav.settings", + "nav.engine"))); + + private PlatformProfileUtils() { + } + + // ─── 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 = 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; + } + + /** 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."); + } + 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); + } + } + + /** 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); + 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); + } + } + + /** 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(); + + SelectQueryStruct qs = new SelectQueryStruct(); + 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")); + + return QueryExecutionUtility.flushRsToMap(securityDb, qs); + } + + // ─── 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( + "Unknown platform feature key: " + featureKey + + ". Valid keys: " + new java.util.TreeSet<>(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); + } + } + + /** 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<>(); + for (String key : PREDEFINED_FEATURE_KEYS) { + result.put(key, Boolean.FALSE); + } + + 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)); + + 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); + } + } + return result; + } + + // ─── 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(); + String actorId = 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); + } + } + + /** + * 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(); + 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 = getUserId(user); + String profileId = getAssignedProfileId(securityDb, userId); + if (profileId == null) { + Map all = new LinkedHashMap<>(); + for (String key : PREDEFINED_FEATURE_KEYS) { + all.put(key, Boolean.TRUE); + } + return all; + } + 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(); + + SelectQueryStruct qs = new SelectQueryStruct(); + 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)); + + 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(); + 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)); + 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)); + return QueryExecutionUtility.flushToString(securityDb, qs); + } +} diff --git a/src/prerna/reactor/platformprofile/RemoveUserPlatformProfileReactor.java b/src/prerna/reactor/platformprofile/RemoveUserPlatformProfileReactor.java new file mode 100644 index 00000000000..2969904dd6a --- /dev/null +++ b/src/prerna/reactor/platformprofile/RemoveUserPlatformProfileReactor.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.platformprofile; + +import prerna.auth.User; +import prerna.auth.utils.SecurityAdminUtils; +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[] { ReactorKeysEnum.USER_ID.getKey() }; + this.keyRequired = new int[] { 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + 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()); + PlatformProfileUtils.removeUserProfile(userId, user); + NounMetadata noun = new NounMetadata(true, PixelDataType.BOOLEAN, PixelOperationType.OPERATION); + 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..a808ef41a8e --- /dev/null +++ b/src/prerna/reactor/platformprofile/SetPlatformFeatureReactor.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.platformprofile; + +import prerna.auth.User; +import prerna.auth.utils.SecurityAdminUtils; +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[] { ReactorKeysEnum.PROFILE_ID.getKey(), ReactorKeysEnum.FEATURE_KEY.getKey(), ReactorKeysEnum.ENABLED.getKey() }; + this.keyRequired = new int[] { 1, 1, 1 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + 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()); + 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, PixelOperationType.OPERATION); + 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..83fa1e68ba1 --- /dev/null +++ b/src/prerna/reactor/platformprofile/UpdatePlatformProfileReactor.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.platformprofile; + +import prerna.auth.User; +import prerna.auth.utils.SecurityAdminUtils; +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[] { ReactorKeysEnum.PROFILE_ID.getKey(), ReactorKeysEnum.NAME.getKey(), ReactorKeysEnum.DESCRIPTION.getKey() }; + this.keyRequired = new int[] { 1, 0, 0 }; + } + + @Override + public NounMetadata execute() { + organizeKeys(); + User user = this.insight.getUser(); + 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()); + 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, PixelOperationType.OPERATION); + noun.addAdditionalReturn(NounMetadata.getSuccessNounMessage("Platform profile updated.")); + return noun; + } + + @Override + public String getReactorDescription() { + return "Update a platform profile."; + } +} diff --git a/src/prerna/sablecc2/om/ReactorKeysEnum.java b/src/prerna/sablecc2/om/ReactorKeysEnum.java index a1a6d730053..636af3c65f4 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"), @@ -91,6 +91,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"), @@ -109,6 +110,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"), @@ -142,6 +145,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"), @@ -235,6 +240,7 @@ 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."), PIXEL("pixel", "Pixel script as string"), @@ -304,6 +310,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"), @@ -324,6 +331,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"), 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",