diff --git a/client/src/pages/Login.jsx b/client/src/pages/Login.jsx
index 12c4479..df49dcb 100644
--- a/client/src/pages/Login.jsx
+++ b/client/src/pages/Login.jsx
@@ -216,17 +216,6 @@ const Login = () => {
)}
- {/* Demo credentials for local/ldap auth */}
- {(tenantConfig?.auth_provider === 'local' || tenantConfig?.auth_provider === 'ldap') && (
-
-
- Demo credentials:
- Username: admin
- Password: admin123
-
-
- )}
-
{/* Error message if auth provider not configured */}
{!tenantConfig?.auth_provider && (
diff --git a/management/src/components/TenantModal.jsx b/management/src/components/TenantModal.jsx
index 3d86853..ea7f363 100644
--- a/management/src/components/TenantModal.jsx
+++ b/management/src/components/TenantModal.jsx
@@ -69,11 +69,16 @@ const TenantModal = ({ isOpen, onClose, tenant = null, onSave }) => {
primary_color: '#3B82F6',
secondary_color: '#1F2937',
company_name: ''
- }
+ },
+ // IP Restriction settings
+ ip_restriction_enabled: false,
+ ip_whitelist: [],
+ ip_restriction_message: 'Access denied. Your IP address is not authorized to access this tenant.'
})
const [showSecrets, setShowSecrets] = useState(false)
const [loading, setLoading] = useState(false)
+ const [newIP, setNewIP] = useState('')
// Load tenant data when editing
useEffect(() => {
@@ -85,7 +90,11 @@ const TenantModal = ({ isOpen, onClose, tenant = null, onSave }) => {
user_mapping: { ...formData.user_mapping, ...tenant.user_mapping },
role_mapping: { ...formData.role_mapping, ...tenant.role_mapping },
features: { ...formData.features, ...tenant.features },
- branding: { ...formData.branding, ...tenant.branding }
+ branding: { ...formData.branding, ...tenant.branding },
+ // IP restriction fields with fallbacks
+ ip_restriction_enabled: tenant.ip_restriction_enabled || false,
+ ip_whitelist: tenant.ip_whitelist || [],
+ ip_restriction_message: tenant.ip_restriction_message || 'Access denied. Your IP address is not authorized to access this tenant.'
})
}
}, [tenant])
@@ -106,6 +115,43 @@ const TenantModal = ({ isOpen, onClose, tenant = null, onSave }) => {
}))
}
+ // IP Management functions
+ const addIPToWhitelist = () => {
+ if (!newIP.trim()) {
+ toast.error('Please enter an IP address or CIDR block')
+ return
+ }
+
+ // Basic validation for IP format
+ const ipPattern = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:\/(?:[0-9]|[1-2][0-9]|3[0-2]))?$|^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|^\d{1,3}\.\d{1,3}\.\d{1,3}\.\*$/
+
+ if (!ipPattern.test(newIP.trim())) {
+ toast.error('Please enter a valid IP address, CIDR block (e.g., 192.168.1.0/24), or wildcard (e.g., 192.168.1.*)')
+ return
+ }
+
+ const ip = newIP.trim()
+ if (formData.ip_whitelist.includes(ip)) {
+ toast.error('This IP is already in the whitelist')
+ return
+ }
+
+ setFormData(prev => ({
+ ...prev,
+ ip_whitelist: [...prev.ip_whitelist, ip]
+ }))
+ setNewIP('')
+ toast.success('IP added to whitelist')
+ }
+
+ const removeIPFromWhitelist = (ipToRemove) => {
+ setFormData(prev => ({
+ ...prev,
+ ip_whitelist: prev.ip_whitelist.filter(ip => ip !== ipToRemove)
+ }))
+ toast.success('IP removed from whitelist')
+ }
+
const handleSubmit = async (e) => {
e.preventDefault()
setLoading(true)
@@ -604,6 +650,120 @@ const TenantModal = ({ isOpen, onClose, tenant = null, onSave }) => {
+ {/* IP Restriction Configuration */}
+
+
IP Access Control
+
+
+ setFormData(prev => ({
+ ...prev,
+ ip_restriction_enabled: e.target.checked
+ }))}
+ className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
+ />
+
+
+
+ {formData.ip_restriction_enabled && (
+
+
+ Only IPs in the whitelist below will be able to access this tenant. You can add individual IP addresses, CIDR blocks, or wildcards.
+
+
+ {/* Add IP Input */}
+
+ setNewIP(e.target.value)}
+ onKeyPress={(e) => {
+ if (e.key === 'Enter') {
+ e.preventDefault()
+ addIPToWhitelist()
+ }
+ }}
+ className="flex-1 border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
+ />
+
+
+
+ {/* IP Whitelist */}
+ {formData.ip_whitelist.length > 0 && (
+
+
+
+ {formData.ip_whitelist.map((ip, index) => (
+
+ {ip}
+
+
+ ))}
+
+
+ )}
+
+ {/* Custom restriction message */}
+
+
+
+
+
+
+
+
+ ⚠️ Important Security Notes
+
+
+
+ - Make sure to include your current IP to avoid being locked out
+ - IP restrictions apply to all tenant access including login and API calls
+ - Use CIDR notation for IP ranges (e.g., 192.168.1.0/24)
+ - Use wildcards for partial matching (e.g., 192.168.1.*)
+
+
+
+
+
+
+ )}
+
+
{/* Form Actions */}