Compare commits

...
Author SHA1 Message Date
Anthony Stirling 71c4419b7e sso in server plan 2025-12-03 13:11:32 +00:00
5 changed files with 53 additions and 21 deletions
@@ -67,19 +67,19 @@ public class CustomSaml2AuthenticationSuccessHandler
boolean userExists = userService.usernameExistsIgnoreCase(username);
// Check if user is eligible for SAML (grandfathered or system has paid license)
// Check if user is eligible for SAML (grandfathered or system has ENTERPRISE license)
if (userExists) {
stirling.software.proprietary.security.model.User user =
userService.findByUsernameIgnoreCase(username).orElse(null);
if (user != null && !licenseSettingsService.isOAuthEligible(user)) {
// User is not grandfathered and no paid license - block SAML login
if (user != null && !licenseSettingsService.isSamlEligible(user)) {
// User is not grandfathered and no ENTERPRISE license - block SAML login
response.sendRedirect(
request.getContextPath() + "/logout?saml2RequiresLicense=true");
return;
}
} else if (!licenseSettingsService.isOAuthEligible(null)) {
// No existing user and no paid license -> block auto creation
} else if (!licenseSettingsService.isSamlEligible(null)) {
// No existing user and no ENTERPRISE license -> block auto creation
response.sendRedirect(
request.getContextPath() + "/logout?saml2RequiresLicense=true");
return;
@@ -331,17 +331,17 @@ public class UserLicenseSettingsService {
}
/**
* Checks if a user is eligible to use OAuth/SAML authentication.
* Checks if a user is eligible to use OAuth authentication.
*
* <p>A user is eligible if:
*
* <ul>
* <li>They are grandfathered for OAuth (existing user before policy change), OR
* <li>The system has an ENTERPRISE license (SSO is enterprise-only)
* <li>The system has a paid license (SERVER or ENTERPRISE)
* </ul>
*
* @param user The user to check
* @return true if the user can use OAuth/SAML
* @return true if the user can use OAuth
*/
public boolean isOAuthEligible(stirling.software.proprietary.security.model.User user) {
// Grandfathered users always have OAuth access
@@ -350,10 +350,36 @@ public class UserLicenseSettingsService {
return true;
}
// Users can use OAuth/SAML only if system has ENTERPRISE license
boolean hasEnterpriseLicense = hasEnterpriseLicense();
log.debug("OAuth eligibility check: hasEnterpriseLicense={}", hasEnterpriseLicense);
return hasEnterpriseLicense;
// Users can use OAuth with SERVER or ENTERPRISE license
boolean hasPaid = hasPaidLicense();
log.debug("OAuth eligibility check: hasPaidLicense={}", hasPaid);
return hasPaid;
}
/**
* Checks if a user is eligible to use SAML authentication.
*
* <p>A user is eligible if:
*
* <ul>
* <li>They are grandfathered for OAuth (existing user before policy change), OR
* <li>The system has an ENTERPRISE license (SAML is enterprise-only)
* </ul>
*
* @param user The user to check
* @return true if the user can use SAML
*/
public boolean isSamlEligible(stirling.software.proprietary.security.model.User user) {
// Grandfathered users always have SAML access
if (user != null && user.isOauthGrandfathered()) {
log.debug("User {} is grandfathered for SAML", user.getUsername());
return true;
}
// Users can use SAML only with ENTERPRISE license
boolean hasEnterprise = hasEnterpriseLicense();
log.debug("SAML eligibility check: hasEnterpriseLicense={}", hasEnterprise);
return hasEnterprise;
}
/**
@@ -500,8 +526,7 @@ public class UserLicenseSettingsService {
}
/**
* Checks if the system has an ENTERPRISE license. Used for enterprise-only features like SSO
* (OAuth/SAML).
* Checks if the system has an ENTERPRISE license. Used for enterprise-only features like SAML.
*
* @return true if ENTERPRISE license is active
*/
@@ -3454,8 +3454,8 @@ signinTitle = "Please sign in"
ssoSignIn = "Login via Single Sign-on"
oAuth2AutoCreateDisabled = "OAUTH2 Auto-Create User Disabled"
oAuth2AdminBlockedUser = "Registration or logging in of non-registered users is currently blocked. Please contact the administrator."
oAuth2RequiresLicense = "OAuth/SSO login requires a paid license (Server or Enterprise). Please contact the administrator to upgrade your plan."
saml2RequiresLicense = "SAML login requires a paid license (Server or Enterprise). Please contact the administrator to upgrade your plan."
oAuth2RequiresLicense = "OAuth/SSO login requires a Server or Enterprise license. Please contact the administrator to upgrade your plan."
saml2RequiresLicense = "SAML login requires an Enterprise license. Please contact the administrator to upgrade your plan."
maxUsersReached = "Maximum number of users reached for your current license. Please contact the administrator to upgrade your plan or add more seats."
oauth2RequestNotFound = "Authorization request not found"
oauth2InvalidUserInfoResponse = "Invalid User Info Response"
@@ -97,6 +97,7 @@ export const OAUTH2_PROVIDERS: Provider[] = [
icon: 'key-rounded',
type: 'oauth2',
scope: 'SSO',
businessTier: false, // Server tier - OAuth2/OIDC SSO
fields: [
{
key: 'issuer',
@@ -141,6 +142,7 @@ export const GENERIC_OAUTH2_PROVIDER: Provider = {
icon: 'link-rounded',
type: 'oauth2',
scope: 'SSO',
businessTier: false, // Server tier - OAuth2/OIDC SSO
fields: [
{
key: 'enabled',
@@ -262,8 +264,8 @@ export const SAML2_PROVIDER: Provider = {
name: 'SAML2',
icon: 'verified-user-rounded',
type: 'saml2',
scope: 'SSO',
businessTier: true,
scope: 'SSO (SAML)',
businessTier: true, // Enterprise tier - SAML only
fields: [
{
key: 'enabled',
@@ -19,6 +19,7 @@ export const PLAN_FEATURES = {
{ name: 'Editing text in pdfs', included: false },
{ name: 'Users limited to seats', included: false },
{ name: 'SSO', included: false },
{ name: 'SAML', included: false },
{ name: 'Auditing', included: false },
{ name: 'Usage tracking', included: false },
{ name: 'Prometheus Support', included: false },
@@ -37,7 +38,8 @@ export const PLAN_FEATURES = {
{ name: 'External Database', included: true },
{ name: 'Editing text in pdfs', included: true },
{ name: 'Users limited to seats', included: false },
{ name: 'SSO', included: false },
{ name: 'SSO', included: true },
{ name: 'SAML', included: false },
{ name: 'Auditing', included: false },
{ name: 'Usage tracking', included: false },
{ name: 'Prometheus Support', included: false },
@@ -57,6 +59,7 @@ export const PLAN_FEATURES = {
{ name: 'Editing text in pdfs', included: true },
{ name: 'Users limited to seats', included: true },
{ name: 'SSO', included: true },
{ name: 'SAML', included: true },
{ name: 'Auditing', included: true },
{ name: 'Usage tracking', included: true },
{ name: 'Prometheus Support', included: true },
@@ -74,6 +77,7 @@ export const PLAN_HIGHLIGHTS = {
'Self-hosted on your infrastructure',
'Unlimited users',
'Advanced integrations',
'SSO (OAuth2/OIDC)',
'Editing text in PDFs',
'Cancel anytime'
],
@@ -81,17 +85,18 @@ export const PLAN_HIGHLIGHTS = {
'Self-hosted on your infrastructure',
'Unlimited users',
'Advanced integrations',
'SSO (OAuth2/OIDC)',
'Editing text in PDFs',
'Save with annual billing'
],
ENTERPRISE_MONTHLY: [
'Enterprise features (SSO, Auditing)',
'Enterprise features (SAML, Auditing)',
'Usage tracking & Prometheus',
'Custom PDF metadata',
'Per-seat licensing'
],
ENTERPRISE_YEARLY: [
'Enterprise features (SSO, Auditing)',
'Enterprise features (SAML, Auditing)',
'Usage tracking & Prometheus',
'Custom PDF metadata',
'Save with annual billing'