Fix jwt-token

This commit is contained in:
2025-09-13 14:23:01 +02:00
parent 02b1b6f62f
commit 181bd9cfa4
4 changed files with 798 additions and 8 deletions

View File

@@ -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);