Fix jwt-token
This commit is contained in:
@@ -457,17 +457,465 @@ const SecuritySettings = ({ tenantConfig, onRefresh }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const AuthenticationSettings = () => (
|
||||
<div className="bg-white shadow rounded-lg">
|
||||
<div className="px-4 py-5 sm:p-6">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900">Authentication Settings</h3>
|
||||
<p className="mt-2 text-sm text-gray-500">
|
||||
Authentication provider configuration will be available here.
|
||||
</p>
|
||||
const AuthenticationSettings = ({ tenantConfig }) => {
|
||||
const [authConfig, setAuthConfig] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [editing, setEditing] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
fetchAuthConfig();
|
||||
}, []);
|
||||
|
||||
const fetchAuthConfig = async () => {
|
||||
try {
|
||||
const response = await api.get('/tenant/auth');
|
||||
setAuthConfig(response.data.data);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch auth config:', error);
|
||||
toast.error('Failed to load authentication settings');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const saveAuthConfig = async (newConfig) => {
|
||||
setSaving(true);
|
||||
try {
|
||||
const response = await api.put('/tenant/auth', newConfig);
|
||||
setAuthConfig(response.data.data);
|
||||
setEditing(false);
|
||||
toast.success('Authentication settings updated successfully');
|
||||
} catch (error) {
|
||||
console.error('Failed to save auth config:', error);
|
||||
toast.error(error.response?.data?.message || 'Failed to save authentication settings');
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="bg-white shadow rounded-lg">
|
||||
<div className="px-4 py-5 sm:p-6">
|
||||
<div className="animate-pulse">
|
||||
<div className="h-4 bg-gray-200 rounded w-1/4 mb-4"></div>
|
||||
<div className="h-4 bg-gray-200 rounded w-3/4 mb-2"></div>
|
||||
<div className="h-4 bg-gray-200 rounded w-1/2"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const authProvider = tenantConfig?.auth_provider || 'local';
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Current Authentication Provider */}
|
||||
<div className="bg-white shadow rounded-lg">
|
||||
<div className="px-4 py-5 sm:p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900">Authentication Provider</h3>
|
||||
<p className="mt-1 text-sm text-gray-500">
|
||||
Current authentication method for this tenant
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-3">
|
||||
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${
|
||||
authProvider === 'local'
|
||||
? 'bg-blue-100 text-blue-800'
|
||||
: authProvider === 'saml'
|
||||
? 'bg-purple-100 text-purple-800'
|
||||
: authProvider === 'oauth'
|
||||
? 'bg-green-100 text-green-800'
|
||||
: authProvider === 'ldap'
|
||||
? 'bg-yellow-100 text-yellow-800'
|
||||
: 'bg-gray-100 text-gray-800'
|
||||
}`}>
|
||||
{authProvider.toUpperCase()}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => setEditing(!editing)}
|
||||
className="inline-flex items-center px-3 py-2 border border-gray-300 shadow-sm text-sm leading-4 font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500"
|
||||
>
|
||||
{editing ? 'Cancel' : 'Configure'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Provider Description */}
|
||||
<div className="mt-4 p-4 bg-gray-50 rounded-md">
|
||||
<p className="text-sm text-gray-600">
|
||||
{authProvider === 'local' && 'Users are managed directly in this system with username/password authentication.'}
|
||||
{authProvider === 'saml' && 'Users authenticate through SAML Single Sign-On (SSO) provider.'}
|
||||
{authProvider === 'oauth' && 'Users authenticate through OAuth provider (Google, Microsoft, etc.).'}
|
||||
{authProvider === 'ldap' && 'Users authenticate through LDAP/Active Directory.'}
|
||||
{authProvider === 'ad' && 'Users authenticate through Active Directory.'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Authentication Configuration */}
|
||||
{editing && (
|
||||
<AuthProviderConfig
|
||||
provider={authProvider}
|
||||
config={authConfig}
|
||||
onSave={saveAuthConfig}
|
||||
onCancel={() => setEditing(false)}
|
||||
saving={saving}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* User Role Mappings */}
|
||||
<div className="bg-white shadow rounded-lg">
|
||||
<div className="px-4 py-5 sm:p-6">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900">Role Mappings</h3>
|
||||
<p className="mt-1 text-sm text-gray-500">
|
||||
Configure how external users are assigned roles in your system
|
||||
</p>
|
||||
|
||||
<div className="mt-6 space-y-4">
|
||||
{authProvider === 'local' ? (
|
||||
<div className="text-sm text-gray-500">
|
||||
Role assignments for local users are managed in the Users tab.
|
||||
</div>
|
||||
) : (
|
||||
<RoleMappingConfig provider={authProvider} config={authConfig} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Session Settings */}
|
||||
<div className="bg-white shadow rounded-lg">
|
||||
<div className="px-4 py-5 sm:p-6">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900">Session Settings</h3>
|
||||
<p className="mt-1 text-sm text-gray-500">
|
||||
Configure session timeout and security settings
|
||||
</p>
|
||||
|
||||
<div className="mt-6">
|
||||
<SessionConfig config={authConfig} onSave={saveAuthConfig} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Auth Provider Configuration Component
|
||||
const AuthProviderConfig = ({ provider, config, onSave, onCancel, saving }) => {
|
||||
const [formData, setFormData] = useState(config || {});
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
onSave(formData);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-white shadow rounded-lg">
|
||||
<div className="px-4 py-5 sm:p-6">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900">
|
||||
{provider.toUpperCase()} Configuration
|
||||
</h3>
|
||||
|
||||
<form onSubmit={handleSubmit} className="mt-6 space-y-6">
|
||||
{provider === 'saml' && (
|
||||
<SAMLConfig formData={formData} setFormData={setFormData} />
|
||||
)}
|
||||
{provider === 'oauth' && (
|
||||
<OAuthConfig formData={formData} setFormData={setFormData} />
|
||||
)}
|
||||
{provider === 'ldap' && (
|
||||
<LDAPConfig formData={formData} setFormData={setFormData} />
|
||||
)}
|
||||
{provider === 'ad' && (
|
||||
<ADConfig formData={formData} setFormData={setFormData} />
|
||||
)}
|
||||
|
||||
<div className="flex justify-end space-x-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onCancel}
|
||||
className="bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={saving}
|
||||
className="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{saving ? 'Saving...' : 'Save Configuration'}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// SAML Configuration
|
||||
const SAMLConfig = ({ formData, setFormData }) => (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700">SSO URL</label>
|
||||
<input
|
||||
type="url"
|
||||
value={formData.sso_url || ''}
|
||||
onChange={(e) => setFormData({...formData, sso_url: e.target.value})}
|
||||
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
|
||||
placeholder="https://your-idp.com/sso"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700">Entity ID</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.entity_id || ''}
|
||||
onChange={(e) => setFormData({...formData, entity_id: e.target.value})}
|
||||
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
|
||||
placeholder="your-app-entity-id"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700">X.509 Certificate</label>
|
||||
<textarea
|
||||
value={formData.certificate || ''}
|
||||
onChange={(e) => setFormData({...formData, certificate: e.target.value})}
|
||||
rows={4}
|
||||
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
|
||||
placeholder="-----BEGIN CERTIFICATE-----"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// OAuth Configuration
|
||||
const OAuthConfig = ({ formData, setFormData }) => (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700">Provider</label>
|
||||
<select
|
||||
value={formData.oauth_provider || 'google'}
|
||||
onChange={(e) => setFormData({...formData, oauth_provider: e.target.value})}
|
||||
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
|
||||
>
|
||||
<option value="google">Google</option>
|
||||
<option value="microsoft">Microsoft</option>
|
||||
<option value="github">GitHub</option>
|
||||
<option value="custom">Custom OAuth Provider</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700">Client ID</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.client_id || ''}
|
||||
onChange={(e) => setFormData({...formData, client_id: e.target.value})}
|
||||
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700">Client Secret</label>
|
||||
<input
|
||||
type="password"
|
||||
value={formData.client_secret || ''}
|
||||
onChange={(e) => setFormData({...formData, client_secret: e.target.value})}
|
||||
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// LDAP Configuration
|
||||
const LDAPConfig = ({ formData, setFormData }) => (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700">LDAP Server</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.ldap_server || ''}
|
||||
onChange={(e) => setFormData({...formData, ldap_server: e.target.value})}
|
||||
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
|
||||
placeholder="ldap://your-server.com:389"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700">Base DN</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.base_dn || ''}
|
||||
onChange={(e) => setFormData({...formData, base_dn: e.target.value})}
|
||||
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
|
||||
placeholder="dc=company,dc=com"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700">Bind DN</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.bind_dn || ''}
|
||||
onChange={(e) => setFormData({...formData, bind_dn: e.target.value})}
|
||||
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
|
||||
placeholder="cn=admin,dc=company,dc=com"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700">Bind Password</label>
|
||||
<input
|
||||
type="password"
|
||||
value={formData.bind_password || ''}
|
||||
onChange={(e) => setFormData({...formData, bind_password: e.target.value})}
|
||||
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// Active Directory Configuration
|
||||
const ADConfig = ({ formData, setFormData }) => (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700">Domain Controller</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.domain_controller || ''}
|
||||
onChange={(e) => setFormData({...formData, domain_controller: e.target.value})}
|
||||
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
|
||||
placeholder="dc.company.com"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700">Domain</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.domain || ''}
|
||||
onChange={(e) => setFormData({...formData, domain: e.target.value})}
|
||||
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
|
||||
placeholder="COMPANY"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700">Service Account</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.service_account || ''}
|
||||
onChange={(e) => setFormData({...formData, service_account: e.target.value})}
|
||||
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
|
||||
placeholder="svc-account@company.com"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700">Service Password</label>
|
||||
<input
|
||||
type="password"
|
||||
value={formData.service_password || ''}
|
||||
onChange={(e) => setFormData({...formData, service_password: e.target.value})}
|
||||
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// Role Mapping Configuration
|
||||
const RoleMappingConfig = ({ provider, config }) => (
|
||||
<div className="space-y-4">
|
||||
<div className="text-sm text-gray-600">
|
||||
Configure how {provider.toUpperCase()} groups/attributes map to system roles:
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
{['admin', 'user_admin', 'security_admin', 'branding_admin', 'operator', 'viewer'].map(role => (
|
||||
<div key={role} className="flex items-center justify-between p-3 border border-gray-200 rounded-md">
|
||||
<div>
|
||||
<span className="font-medium text-gray-900">{role}</span>
|
||||
<span className="ml-2 text-sm text-gray-500">
|
||||
{role === 'admin' && '(Full system access)'}
|
||||
{role === 'user_admin' && '(User management only)'}
|
||||
{role === 'security_admin' && '(Security settings only)'}
|
||||
{role === 'branding_admin' && '(Branding settings only)'}
|
||||
{role === 'operator' && '(Basic operations)'}
|
||||
{role === 'viewer' && '(Read-only access)'}
|
||||
</span>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
placeholder={provider === 'saml' ? 'SAML attribute value' : 'Group name'}
|
||||
className="ml-4 block w-48 border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// Session Configuration
|
||||
const SessionConfig = ({ config, onSave }) => {
|
||||
const [sessionSettings, setSessionSettings] = useState({
|
||||
session_timeout: config?.session_timeout || 480, // 8 hours default
|
||||
require_mfa: config?.require_mfa || false,
|
||||
allow_concurrent_sessions: config?.allow_concurrent_sessions || true
|
||||
});
|
||||
|
||||
const handleSave = () => {
|
||||
onSave({ ...config, ...sessionSettings });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700">Session Timeout (minutes)</label>
|
||||
<input
|
||||
type="number"
|
||||
value={sessionSettings.session_timeout}
|
||||
onChange={(e) => setSessionSettings({...sessionSettings, session_timeout: parseInt(e.target.value)})}
|
||||
className="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
|
||||
min="15"
|
||||
max="1440"
|
||||
/>
|
||||
<p className="mt-1 text-xs text-gray-500">Users will be logged out after this period of inactivity</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center">
|
||||
<input
|
||||
id="require-mfa"
|
||||
type="checkbox"
|
||||
checked={sessionSettings.require_mfa}
|
||||
onChange={(e) => setSessionSettings({...sessionSettings, require_mfa: e.target.checked})}
|
||||
className="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded"
|
||||
/>
|
||||
<label htmlFor="require-mfa" className="ml-2 block text-sm text-gray-900">
|
||||
Require Multi-Factor Authentication
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center">
|
||||
<input
|
||||
id="concurrent-sessions"
|
||||
type="checkbox"
|
||||
checked={sessionSettings.allow_concurrent_sessions}
|
||||
onChange={(e) => setSessionSettings({...sessionSettings, allow_concurrent_sessions: e.target.checked})}
|
||||
className="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded"
|
||||
/>
|
||||
<label htmlFor="concurrent-sessions" className="ml-2 block text-sm text-gray-900">
|
||||
Allow Concurrent Sessions
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={handleSave}
|
||||
className="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500"
|
||||
>
|
||||
Save Session Settings
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const UsersSettings = ({ tenantConfig, onRefresh }) => {
|
||||
const [users, setUsers] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
Reference in New Issue
Block a user