dolibarr user
This commit is contained in:
parent
75854b3b28
commit
9cf463e7fc
@ -95,6 +95,12 @@ Use these functions for all email formatting needs.
|
||||
- **Status**: Removed
|
||||
- **Replacement**: Use `app/api/courrier/send/route.ts` instead.
|
||||
|
||||
### 4. `DELETE /api/users/[userId]` (DEPRECATED)
|
||||
- **Status**: Deprecated but maintained for backward compatibility
|
||||
- **Replacement**: Use `DELETE /api/users?id=[userId]&email=[userEmail]` instead
|
||||
- **Reason**: The new endpoint format supports deletion across all integrated systems (Keycloak, Leantime, and Dolibarr)
|
||||
- **Notes**: The deprecated endpoint now forwards requests to the new endpoint but developers should update their code to use the new format directly
|
||||
|
||||
## Deprecated Components
|
||||
|
||||
### ComposeEmail (components/ComposeEmail.tsx) (REMOVED)
|
||||
|
||||
26
README.md
26
README.md
@ -80,3 +80,29 @@ This centralized approach prevents formatting inconsistencies and direction prob
|
||||
Several functions have been deprecated and removed in favor of centralized implementations:
|
||||
|
||||
- Check the `DEPRECATED_FUNCTIONS.md` file for a complete list of deprecated functions and their replacements.
|
||||
|
||||
## User Management API
|
||||
|
||||
The application provides endpoints for managing users in multiple systems:
|
||||
|
||||
- **Create User**:
|
||||
- Endpoint: `POST /api/users`
|
||||
- Creates users in Keycloak, Leantime, and Dolibarr (if they have "mediation" or "expression" roles)
|
||||
|
||||
- **Update User**:
|
||||
- Endpoint: `PUT /api/users/[userId]`
|
||||
- Updates user details in Keycloak
|
||||
|
||||
- **Delete User**:
|
||||
- Endpoint: `DELETE /api/users?id=[userId]&email=[userEmail]`
|
||||
- Deletes users from Keycloak, Leantime, and Dolibarr systems
|
||||
- **Important**: Always include both `id` and `email` parameters for complete deletion across all systems
|
||||
- The legacy endpoint `DELETE /api/users/[userId]` forwards to the above endpoint
|
||||
|
||||
- **Manage Roles**:
|
||||
- Endpoint: `PUT /api/users/[userId]/roles`
|
||||
- Updates user roles in Keycloak
|
||||
|
||||
- **Reset Password**:
|
||||
- Endpoint: `PUT /api/users/[userId]/password`
|
||||
- Resets user password in Keycloak
|
||||
@ -174,38 +174,34 @@ export async function DELETE(
|
||||
const userDetails = await userResponse.json();
|
||||
console.log('Processing user deletion for ID:', params.userId);
|
||||
|
||||
// Delete user from Keycloak
|
||||
const deleteResponse = await fetch(
|
||||
`${process.env.KEYCLOAK_BASE_URL}/admin/realms/${process.env.KEYCLOAK_REALM}/users/${params.userId}`,
|
||||
{
|
||||
// Forward the request to the new endpoint format with the email parameter
|
||||
// This ensures Dolibarr deletion is also handled
|
||||
const apiUrl = new URL(`${req.headers.get('origin') || process.env.NEXTAUTH_URL}/api/users`);
|
||||
apiUrl.searchParams.append('id', params.userId);
|
||||
apiUrl.searchParams.append('email', userDetails.email);
|
||||
|
||||
const forwardResponse = await fetch(apiUrl.toString(), {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokenData.access_token}`,
|
||||
"Cookie": req.headers.get('cookie') || '',
|
||||
"Authorization": req.headers.get('authorization') || '',
|
||||
},
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
if (!deleteResponse.ok) {
|
||||
console.error("Keycloak delete error");
|
||||
if (!forwardResponse.ok) {
|
||||
const errorData = await forwardResponse.json();
|
||||
console.error("Error forwarding delete request:", errorData);
|
||||
return NextResponse.json(
|
||||
{ error: "Erreur lors de la suppression de l'utilisateur" },
|
||||
{ status: deleteResponse.status }
|
||||
{ error: "Erreur lors de la suppression de l'utilisateur", details: errorData },
|
||||
{ status: forwardResponse.status }
|
||||
);
|
||||
}
|
||||
|
||||
// Delete user from Leantime
|
||||
const leantimeResult = await deleteLeantimeUser(userDetails.email, session.user.id);
|
||||
|
||||
if (!leantimeResult.success) {
|
||||
console.error("Leantime user deletion failed");
|
||||
// We don't return an error here since Keycloak user was deleted successfully
|
||||
// We just log the error and continue
|
||||
}
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
const responseData = await forwardResponse.json();
|
||||
return NextResponse.json(responseData);
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error deleting user");
|
||||
console.error("Error deleting user:", error);
|
||||
return NextResponse.json(
|
||||
{ error: "Erreur serveur" },
|
||||
{ status: 500 }
|
||||
|
||||
273
scripts/test-user-deletion.js
Normal file
273
scripts/test-user-deletion.js
Normal file
@ -0,0 +1,273 @@
|
||||
/**
|
||||
* Test script for verifying user deletion across all integrated systems
|
||||
*
|
||||
* This script creates a test user with mediation role, verifies it exists in all systems,
|
||||
* then deletes it and verifies deletion in all systems.
|
||||
*
|
||||
* Usage: node scripts/test-user-deletion.js
|
||||
*/
|
||||
|
||||
require('dotenv').config();
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
// Test user configuration
|
||||
const TEST_USER = {
|
||||
username: `test-user-${Date.now()}`,
|
||||
firstName: 'Test',
|
||||
lastName: 'User',
|
||||
email: `test-user-${Date.now()}@example.com`,
|
||||
password: 'password123',
|
||||
roles: ['mediation'] // Using mediation role which should be created in Dolibarr
|
||||
};
|
||||
|
||||
// Configuration from environment variables
|
||||
const config = {
|
||||
keycloak: {
|
||||
baseUrl: process.env.KEYCLOAK_BASE_URL,
|
||||
realm: process.env.KEYCLOAK_REALM,
|
||||
clientId: process.env.KEYCLOAK_CLIENT_ID,
|
||||
clientSecret: process.env.KEYCLOAK_CLIENT_SECRET
|
||||
},
|
||||
leantime: {
|
||||
apiUrl: 'https://agilite.slm-lab.net/api/jsonrpc',
|
||||
apiKey: process.env.LEANTIME_TOKEN
|
||||
},
|
||||
dolibarr: {
|
||||
apiUrl: process.env.DOLIBARR_API_URL,
|
||||
apiKey: process.env.DOLIBARR_API_KEY
|
||||
},
|
||||
nextAuthUrl: process.env.NEXTAUTH_URL || 'http://localhost:3000'
|
||||
};
|
||||
|
||||
// Helper to get admin token for Keycloak operations
|
||||
async function getKeycloakAdminToken() {
|
||||
const response = await fetch(
|
||||
`${config.keycloak.baseUrl}/realms/${config.keycloak.realm}/protocol/openid-connect/token`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: new URLSearchParams({
|
||||
grant_type: 'client_credentials',
|
||||
client_id: config.keycloak.clientId,
|
||||
client_secret: config.keycloak.clientSecret,
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
const data = await response.json();
|
||||
if (!response.ok || !data.access_token) {
|
||||
throw new Error('Failed to get Keycloak admin token');
|
||||
}
|
||||
|
||||
return data.access_token;
|
||||
}
|
||||
|
||||
// Create a test user in all systems via the API
|
||||
async function createTestUser() {
|
||||
console.log(`Creating test user: ${TEST_USER.username} (${TEST_USER.email})`);
|
||||
|
||||
const response = await fetch(`${config.nextAuthUrl}/api/users`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(TEST_USER),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(`Failed to create test user: ${JSON.stringify(error)}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log('User created successfully:', data);
|
||||
return data.user;
|
||||
}
|
||||
|
||||
// Check if user exists in Keycloak
|
||||
async function checkKeycloakUser(userId) {
|
||||
console.log(`Checking if user exists in Keycloak (ID: ${userId})`);
|
||||
const token = await getKeycloakAdminToken();
|
||||
|
||||
const response = await fetch(
|
||||
`${config.keycloak.baseUrl}/admin/realms/${config.keycloak.realm}/users/${userId}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (response.status === 404) {
|
||||
console.log('User not found in Keycloak');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
console.error('Error checking Keycloak user:', await response.text());
|
||||
return null; // Error state
|
||||
}
|
||||
|
||||
console.log('User exists in Keycloak');
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if user exists in Leantime
|
||||
async function checkLeantimeUser(email) {
|
||||
console.log(`Checking if user exists in Leantime (Email: ${email})`);
|
||||
|
||||
try {
|
||||
const response = await fetch(config.leantime.apiUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-API-Key': config.leantime.apiKey,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
method: 'leantime.rpc.Users.Users.getUserIdByEmail',
|
||||
jsonrpc: '2.0',
|
||||
id: 1,
|
||||
params: {
|
||||
email: email
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
if (!response.ok || !data.result) {
|
||||
console.log('User not found in Leantime');
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log('User exists in Leantime');
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Error checking Leantime user:', error);
|
||||
return null; // Error state
|
||||
}
|
||||
}
|
||||
|
||||
// Check if user exists in Dolibarr
|
||||
async function checkDolibarrUser(email) {
|
||||
console.log(`Checking if user exists in Dolibarr (Email: ${email})`);
|
||||
|
||||
try {
|
||||
const apiUrl = config.dolibarr.apiUrl.endsWith('/')
|
||||
? config.dolibarr.apiUrl
|
||||
: `${config.dolibarr.apiUrl}/`;
|
||||
|
||||
const response = await fetch(
|
||||
`${apiUrl}users?sortfield=t.rowid&sortorder=ASC&limit=1&sqlfilters=(t.email:=:'${encodeURIComponent(email)}')`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'DOLAPIKEY': config.dolibarr.apiKey,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
console.error('Error response from Dolibarr:', await response.text());
|
||||
return null; // Error state
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
if (!Array.isArray(data) || data.length === 0) {
|
||||
console.log('User not found in Dolibarr');
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log('User exists in Dolibarr with ID:', data[0].id);
|
||||
return { exists: true, id: data[0].id };
|
||||
} catch (error) {
|
||||
console.error('Error checking Dolibarr user:', error);
|
||||
return null; // Error state
|
||||
}
|
||||
}
|
||||
|
||||
// Delete user from all systems via the API
|
||||
async function deleteTestUser(userId, email) {
|
||||
console.log(`Deleting test user: ID=${userId}, Email=${email}`);
|
||||
|
||||
const response = await fetch(
|
||||
`${config.nextAuthUrl}/api/users?id=${userId}&email=${encodeURIComponent(email)}`,
|
||||
{
|
||||
method: 'DELETE',
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(`Failed to delete test user: ${JSON.stringify(error)}`);
|
||||
}
|
||||
|
||||
console.log('User deletion request successful');
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
// Main test function
|
||||
async function runTest() {
|
||||
try {
|
||||
console.log('=== STARTING USER DELETION TEST ===');
|
||||
|
||||
// Step 1: Create a test user
|
||||
console.log('\n=== Step 1: Creating test user ===');
|
||||
const createdUser = await createTestUser();
|
||||
console.log(`Test user created with ID: ${createdUser.id}`);
|
||||
|
||||
// Wait a moment for systems to process
|
||||
console.log('Waiting for systems to process...');
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
// Step 2: Verify user exists in all systems
|
||||
console.log('\n=== Step 2: Verifying user exists in all systems ===');
|
||||
const keycloakExists = await checkKeycloakUser(createdUser.id);
|
||||
const leantimeExists = await checkLeantimeUser(TEST_USER.email);
|
||||
const dolibarrUser = await checkDolibarrUser(TEST_USER.email);
|
||||
|
||||
if (keycloakExists === null || leantimeExists === null || dolibarrUser === null) {
|
||||
throw new Error('Error checking user existence in integrated systems');
|
||||
}
|
||||
|
||||
if (!keycloakExists || !leantimeExists || !dolibarrUser.exists) {
|
||||
throw new Error('User not created in all systems properly');
|
||||
}
|
||||
|
||||
console.log('User confirmed to exist in all integrated systems');
|
||||
|
||||
// Step 3: Delete the test user
|
||||
console.log('\n=== Step 3: Deleting test user ===');
|
||||
await deleteTestUser(createdUser.id, TEST_USER.email);
|
||||
|
||||
// Wait a moment for systems to process
|
||||
console.log('Waiting for systems to process deletion...');
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
// Step 4: Verify user has been deleted from all systems
|
||||
console.log('\n=== Step 4: Verifying user deletion from all systems ===');
|
||||
const keycloakDeleted = !(await checkKeycloakUser(createdUser.id));
|
||||
const leantimeDeleted = !(await checkLeantimeUser(TEST_USER.email));
|
||||
const dolibarrDeleted = !(await checkDolibarrUser(TEST_USER.email)).exists;
|
||||
|
||||
console.log('\n=== TEST RESULTS ===');
|
||||
console.log(`Keycloak user deleted: ${keycloakDeleted ? 'YES' : 'NO'}`);
|
||||
console.log(`Leantime user deleted: ${leantimeDeleted ? 'YES' : 'NO'}`);
|
||||
console.log(`Dolibarr user deleted: ${dolibarrDeleted ? 'YES' : 'NO'}`);
|
||||
|
||||
if (keycloakDeleted && leantimeDeleted && dolibarrDeleted) {
|
||||
console.log('\n✅ TEST PASSED: User successfully deleted from all systems');
|
||||
} else {
|
||||
console.log('\n❌ TEST FAILED: User not deleted from all systems');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n❌ TEST ERROR:', error);
|
||||
}
|
||||
|
||||
console.log('\n=== TEST COMPLETED ===');
|
||||
}
|
||||
|
||||
// Run the test
|
||||
runTest();
|
||||
Loading…
Reference in New Issue
Block a user