Skip to content

feat: role-based feature visibility — App Profiles + Platform Profiles - #2668

Closed
ppatel9703 wants to merge 3 commits into
devfrom
feat/role-based-feature-visibility
Closed

feat: role-based feature visibility — App Profiles + Platform Profiles#2668
ppatel9703 wants to merge 3 commits into
devfrom
feat/role-based-feature-visibility

Conversation

@ppatel9703

@ppatel9703 ppatel9703 commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Description

Implements a two-part Role-Based Feature Visibility system from scratch. The system has two independent parts:

  • Part A — App Profiles (fail-closed): controls which features are visible inside a specific app for a given user
  • Part B — Platform Profiles (fail-open): controls which top-level nav pages a user can see after login

"Profile" is intentionally distinct from SEMOSS's existing "role" concept (owner/editor/viewer). Roles control what a user can do; profiles control what a user sees.


Changes Made

New Utility Classes

  • src/prerna/auth/utils/AppProfileUtils.java — All DB operations for app-level profiles, features, and user-profile assignments. Cascade delete from SecurityProjectUtils ensures APP_USER_PROFILE rows are cleaned up whenever a user loses app access.
  • src/prerna/auth/utils/PlatformProfileUtils.java — All DB operations for platform-level profiles and nav feature toggles. Validates feature keys against a predefined set (nav.app-catalog, nav.build, nav.skills, nav.settings, nav.engine).

Security DB Schema (AbstractSecurityUtils.java)

7 new tables added to initialize() using the existing createTableIfNotExists pattern:

  • APP_PROFILE, APP_FEATURE, APP_PROFILE_FEATURE, APP_USER_PROFILE
  • PLATFORM_PROFILE, PLATFORM_PROFILE_FEATURE, PLATFORM_USER_PROFILE

App Profile Reactors (src/prerna/reactor/appprofile/)

16 reactors covering full CRUD for profiles, features, profile-feature assignment, and user-profile assignment:

Reactor Keys Auth
CreateAppProfile app, name, description, isDefault canManageProfiles
UpdateAppProfile app, profileId, name, description, isDefault canManageProfiles
DeleteAppProfile app, profileId canManageProfiles (rejects if users assigned)
GetAppProfiles app* canManageProfiles
CreateAppFeature app, key, description canManageProfiles
UpdateAppFeature app, featureId, key, description canManageProfiles
DeleteAppFeature app, featureId canManageProfiles
GetAppFeatures app* canManageProfiles
SetProfileFeature app, profileId, featureId, enabled canManageProfiles
GetProfileFeatures app, profileId canManageProfiles
AssignUserProfile app, userId, profileId* canManageProfiles
RemoveUserProfile app, userId canManageProfiles
GetUserProfile app, userId canManageProfiles
GetProfileUsers app, profileId canManageProfiles
CheckFeature app, featureKey canEvaluateFeatures (any app user)
GetUserFeatures app* canEvaluateFeatures — returns enabled features only

Platform Profile Reactors (src/prerna/reactor/platformprofile/)

10 reactors (all admin-only):

Reactor Keys
CreatePlatformProfile name*, description
UpdatePlatformProfile profileId*, name, description
DeletePlatformProfile profileId* (rejects if users assigned)
GetPlatformProfiles (none)
SetPlatformFeature profileId, featureKey, enabled*
GetPlatformFeatures profileId*
AssignUserPlatformProfile userId, profileId
RemoveUserPlatformProfile userId*
GetUserPlatformFeatures (none — uses session user)
GetPlatformProfileUsers profileId*

SecurityProjectUtils.java

Added AppProfileUtils.removeUserProfile(appId, userId) at every code path that removes a user from a project — ensures no stale APP_USER_PROFILE rows after access revocation.


How to Test

App Profiles — end-to-end

# 1. Create profiles (replace "abc" with a real app ID you own)
CreateAppProfile(app="abc", name="beta", isDefault=false);
CreateAppProfile(app="abc", name="standard", isDefault=true);

# 2. Create a feature
CreateAppFeature(app="abc", key="export-csv");

# 3. Enable feature for beta profile only
SetProfileFeature(app="abc", profileId="<beta-id>", featureId="<feature-id>", enabled=true);

# 4. Assign a user to the beta profile
AssignUserProfile(app="abc", userId="<user-id>", profileId="<beta-id>");

# 5. As that user, check the feature (should return true)
CheckFeature(app="abc", featureKey="export-csv");

# 6. GetUserFeatures — should return only enabled features
GetUserFeatures(app="abc");

# 7. Remove user from profile; CheckFeature falls back to standard (default) → false
RemoveUserProfile(app="abc", userId="<user-id>");
CheckFeature(app="abc", featureKey="export-csv");

# 8. Backwards compat — app with no profiles/features
CheckFeature(app="no-profile-app", featureKey="anything");
# → false (no features defined, not an error)

Platform Profiles

# Create profile
CreatePlatformProfile(name="catalog-only");

# Enable only app-catalog nav
SetPlatformFeature(profileId="<id>", featureKey="nav.app-catalog", enabled=true);

# Assign user
AssignUserPlatformProfile(userId="<user-id>", profileId="<id>");

# Query features for that user (after they log in)
GetUserPlatformFeatures();
# → {nav.app-catalog: true, nav.build: false, nav.skills: false, ...}

# Unassigned user (no platform profile) gets all nav visible (fail-open)
# → all keys = true

Delete protection

# Assign a user to a profile, then try to delete it → error with count
AssignUserProfile(app="abc", userId="<user-id>", profileId="<beta-id>");
DeleteAppProfile(app="abc", profileId="<beta-id>");
# → "Cannot delete: 1 users are assigned to this profile. Reassign them first."

Cascade delete (access revocation)

Remove a user from an app via existing security mechanisms and verify the APP_USER_PROFILE row is also gone:

-- After removing user from app:
SELECT * FROM APP_USER_PROFILE WHERE APP_ID='abc' AND USER_ID='<user-id>';
-- → 0 rows

Notes

  • Fail-closed vs fail-open: App profiles are fail-closed (no profile = no features). Platform profiles are fail-open (no profile = all nav visible). This is intentional — app feature visibility is opt-in; platform nav restriction requires explicit admin setup.
  • Phase 2 deferred: Platform profile enforcement at the server/auth layer is not implemented. Users who directly navigate to a gated URL can still access it. UI-only gating per spec.
  • No migration needed: No existing data or tables are modified. New tables are created only if they don't exist.
  • Feature key format: App feature keys validate against ^[a-zA-Z0-9\-]+$, max 100 chars, server-side. Platform feature keys are a closed predefined set.
  • No shared feature key library: App feature keys are per-app. export-csv in App A and App B are separate rows with separate UUIDs.

…rm Profiles)

Part A — App Profiles:
- AbstractSecurityUtils: 4 new tables (APP_PROFILE, APP_FEATURE,
  APP_PROFILE_FEATURE, APP_USER_PROFILE) bootstrapped in initialize()
- AppProfileUtils: full CRUD for profiles, features, profile-feature
  assignments, user-profile assignments, and feature evaluation
  (checkFeature, getUserFeatures — enabled-only, fail-closed)
- 16 reactors in prerna.reactor.appprofile covering all Pixel operations
- SecurityProjectUtils: cascade deleteUserProfile calls in removeProjectUser,
  removeExpiredProjectUser, and removeProjectUsers to prevent stale assignments

Part B — Platform Profiles:
- AbstractSecurityUtils: 3 new tables (PLATFORM_PROFILE,
  PLATFORM_PROFILE_FEATURE, PLATFORM_USER_PROFILE)
- PlatformProfileUtils: CRUD for platform profiles, predefined nav-key
  feature toggles, user assignments, and getUserFeatures (fail-open for
  unassigned users)
- 9 reactors in prerna.reactor.platformprofile

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ppatel9703
ppatel9703 requested a review from a team as a code owner June 24, 2026 17:55
@snyk-io

snyk-io Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

Patel, Parth and others added 2 commits June 24, 2026 15:51
getProfiles() and getFeatures() used SelectQueryStruct/WrapperManager
which performs OWL metadata lookups. APP_PROFILE and APP_FEATURE are
not registered in SEMOSS's schema, so getPhysicalPropertyNameFromConceptualName
returns null causing NPE during SQL composition.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add getPlatformProfileUsers() to PlatformProfileUtils — queries
  PLATFORM_USER_PROFILE for all users assigned to a given profile
- Add GetPlatformProfileUsersReactor (admin-only) so the admin UI
  Members tab can list users assigned to each platform profile

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ppatel9703

Copy link
Copy Markdown
Contributor Author

Closing in favor of #2669 (feat/ui-profiles-rework) which contains the same changes plus subsequent fixes. Please review #2669.

@ppatel9703 ppatel9703 closed this Jun 25, 2026
@ppatel9703
ppatel9703 deleted the feat/role-based-feature-visibility branch June 25, 2026 12:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant