Skip to content

Commit

Permalink
Small testing
Browse files Browse the repository at this point in the history
  • Loading branch information
Gijsdeman committed Aug 21, 2023
1 parent a26f9e8 commit 792fda6
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 129 deletions.
26 changes: 10 additions & 16 deletions src/dovecotAPI.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import axios, { AxiosInstance } from 'axios';
import {
DoveadmExchangeResult,
DovecotData,
DovecotRequestData,
DovecotResponseExchange,
DovecotPermissions,
ActiveDirectoryPermissions,
} from './types';
Expand All @@ -12,7 +11,6 @@ let dovecotClient: AxiosInstance;

export async function initializeDovecotAPI(): Promise<void> {
dovecotClient = axios.create({
// baseURL: `${config.DOVEADM_API_HOST}/doveadm/v1`,
baseURL: 'http://172.22.1.250:9000/doveadm/v1',
headers: {
'Content-Type': 'text/plain',
Expand All @@ -25,9 +23,8 @@ export async function initializeDovecotAPI(): Promise<void> {
* Get all mailboxes of an email
* @param email - email to get all inboxes from
*/
async function getMailboxes(email: string): Promise<string[]> {
// Get all mailboxes
const response = (await dovecotClient.post(
async function getMailboxSubfolders(email: string): Promise<string[]> {
const subFolders: DovecotData[] = ((await dovecotClient.post(
'',
[[
'mailboxList',
Expand All @@ -36,15 +33,13 @@ async function getMailboxes(email: string): Promise<string[]> {
},
`mailboxList_${email}`,
]],
)).data as DovecotResponseExchange;
)).data)[0][1];

// Convert response to array of mailboxes
const mailboxObjects: DoveadmExchangeResult[] = response[0][1];

return mailboxObjects.filter(function (item) {
return !item.mailbox.startsWith('Shared');
}).map((item: DoveadmExchangeResult) => {
return item.mailbox;
// TODO change to reduce
return subFolders.filter(function (subFolder: DovecotData) {
return !subFolder.mailbox.startsWith('Shared');
}).map((subFolder: DovecotData) => {
return subFolder.mailbox;
});
}

Expand Down Expand Up @@ -74,7 +69,7 @@ export async function setDovecotPermissions(mail: string, users: string[], permi
}

if (permission == ActiveDirectoryPermissions.mailPermRO || ActiveDirectoryPermissions.mailPermRW) {
mailboxSubFolders = await getMailboxes(mail);
mailboxSubFolders = await getMailboxSubfolders(mail);
permissionTag = 'PermRO';
}

Expand Down Expand Up @@ -103,7 +98,6 @@ export async function setDovecotPermissions(mail: string, users: string[], permi
}

const dovecotRequest: DovecotRequestData = [
// Check if users should be removed or added
removePermission ? 'aclRemove' : 'aclSet',
{
'user': mail,
Expand Down
60 changes: 29 additions & 31 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Client } from 'ldapts';
import {
activityUserDB,
addUserDB,
updateLocalUserActivity,
createLocalUser,
getLocalUser,
createSOBDB,
getChangedSOBDB,
getUpdateSOBLocalUsers,
getUncheckedLocalActiveUsers,
initializeLocalUserDatabase,
updateLocalUserPermissions,
Expand All @@ -17,9 +17,9 @@ import fs, { PathLike } from 'fs';
import path from 'path';
import { SearchResult } from 'ldapts/Client';
import {
addUserAPI,
createMailcowUser,
getMailcowUser,
editUserAPI,
editMailcowUser,
initializeMailcowAPI,
} from './mailcowAPI';
import {
Expand Down Expand Up @@ -131,17 +131,18 @@ async function synchronizeUserSOB(activeDirectoryUser: ActiveDirectoryUser): Pro
// Construct list in database with DN of all committees they are in
// Get existing list of committees, add new DN as string
// TODO check, I think this can be the same as syncACL where you just pick first entry instead of looping
console.log(activeDirectoryGroup.length);
for (const members of activeDirectoryGroup) {
// Singular entries are possible, so turn them into an array
if (!Array.isArray(members.memberFlattened))
members.memberFlattened = [members.memberFlattened];

for (const member of members.memberFlattened) {
const memberResults: ActiveDirectoryUser[] = (await activeDirectoryConnector.search(member, {
const memberResults: ActiveDirectoryUser = ((await activeDirectoryConnector.search(member, {
scope: 'sub',
attributes: ['mail'],
})).searchEntries as unknown as ActiveDirectoryUser[];
await createSOBDB(memberResults[0].mail, activeDirectoryUser.mail);
})).searchEntries)[0] as unknown as ActiveDirectoryUser;
await createSOBDB(memberResults.mail, activeDirectoryUser.mail);
}
}
}
Expand Down Expand Up @@ -299,7 +300,7 @@ async function getUserDataFromActiveDirectory(): Promise<void> {
})).searchEntries as unknown as ActiveDirectoryUser[];
}
if (retryCount === maxRetryCount) throw new Error('Ran into an issue when getting users from Active Directory.');
console.log('Successfully got all users from Active Directory');
console.log('Successfully got all users from Active Directory. \n\n');
}


Expand All @@ -320,38 +321,39 @@ async function synchronizeUsersWithActiveDirectory(): Promise<void> {

if (!localUser.exists) {
console.log(`Adding local user ${mail} (active: ${isActive})`);
await addUserDB(mail, displayName, isActive);
await createLocalUser(mail, displayName, isActive);
localUser.exists = true;
localUser.isActive = isActive;
}

if (!mailcowUser.exists) {
console.log(`Adding Mailcow user ${mail} (active: ${isActive})`);
await addUserAPI(mail, displayName, isActive, 256);
await createMailcowUser(mail, displayName, isActive, 256);
mailcowUser.exists = true;
mailcowUser.isActive = isActive;
mailcowUser.displayName = displayName;
}

if (localUser.isActive !== isActive) {
console.log(`Set ${mail} to active state ${isActive} in local user database`);
await activityUserDB(mail, isActive, 0);
await updateLocalUserActivity(mail, isActive, 0);
}

if (mailcowUser.isActive !== isActive) {
console.log(`Set ${mail} to active state ${isActive} in Mailcow`);
await editUserAPI(mail, { active: isActive });
await editMailcowUser(mail, { active: isActive });
}

if (mailcowUser.displayName !== displayName) {
console.log(`Changed displayname for ${mail} to ${displayName} in Mailcow`);
await editUserAPI(mail, { name: displayName });
await editMailcowUser(mail, { name: displayName });
}
} catch (error) {
console.error(`Ran into an issue when syncing user ${activeDirectoryUser.mail}: ${error}`);
console.error(`Ran into an issue when syncing user ${activeDirectoryUser.mail}. \n\n ${error}`);
}
}

// Users that were not checked might have to be removed from mailcow
for (const user of await getUncheckedLocalActiveUsers()) {
try {
const mailcowUserData: MailcowUserData = await getMailcowUser(user.email);
Expand All @@ -363,22 +365,21 @@ async function synchronizeUsersWithActiveDirectory(): Promise<void> {

if (inactiveCount > maxInactiveCount) {
console.log(`Deactivated user ${user.email} in local user database, not found in LDAP`);
await activityUserDB(user.email, 0, 255);
await updateLocalUserActivity(user.email, 0, 255);
} else {
console.log(`Increased inactive count to ${inactiveCount + 1} for ${user.email}`);
await activityUserDB(user.email, 2, inactiveCount + 1);
await updateLocalUserActivity(user.email, 2, inactiveCount + 1);
}

// Check if user is still active, if so, deactivate user
if (mailcowUserData.isActive && localUserData.isActive === 0) {
console.log(`Deactivated user ${user.email} in Mailcow, not found in LDAP`);
await editUserAPI(user.email, { active: 0 });
console.log(`Deactivated user ${user.email} in Mailcow, not found in Active Directory`);
await editMailcowUser(user.email, { active: 0 });
}
} catch (error) {
console.log(`Exception throw during handling of ${user}: ${error}`);
console.log(`Ran into an issue when checking inactivity of ${user.email}. \n\n ${error}`);
}
}
console.log('Successfully synced all users with Active Directory');
console.log('Successfully synced all users with Active Directory. \n\n');
}

async function synchronizePermissionsWithActiveDirectory(): Promise<void> {
Expand All @@ -396,22 +397,22 @@ async function synchronizePermissionsWithActiveDirectory(): Promise<void> {
if (activeDirectoryUser[ActiveDirectoryPermissions.mailPermSOB].length != 0)
await synchronizeUserSOB(activeDirectoryUser);
} catch (error) {
console.log(`Ran into an issue when syncing permissions of ${activeDirectoryUser.mail}: ${error}`);
console.log(`Ran into an issue when syncing permissions of ${activeDirectoryUser.mail}. \n\n ${error}`);
}
}

for (const activeDirectoryUser of await getChangedSOBDB()) {
for (const activeDirectoryUser of await getUpdateSOBLocalUsers()) {
try {
console.log(`Changing SOB of ${activeDirectoryUser.email}`);
const SOBs: string[] = activeDirectoryUser.mailPermSOB.split(';');
await editUserAPI(activeDirectoryUser.email, { sender_acl: SOBs });
await editMailcowUser(activeDirectoryUser.email, { sender_acl: SOBs });
await editUserSignature(activeDirectoryUser, SOBs);
} catch (error) {
console.log(`Exception throw during handling of ${activeDirectoryUser}: ${error}`);
console.log(`Ran into an issue when syncing send on behalf of ${activeDirectoryUser.email}. \n\n ${error}`);
}
}

console.log('Successfully synced all permissions with Active Directory');
console.log('Successfully synced all permissions with Active Directory. \n\n');
}

/**
Expand Down Expand Up @@ -449,7 +450,7 @@ async function initializeSync(): Promise<void> {
.catch((error) => {
throw new Error('Ran into an issue when reading extra.conf. \n\n' + error);
});
console.log('Successfully adjusted all template files.');
console.log('Successfully adjusted all template files. \n\n');

console.log(consoleLogLine + '\n APPLYING CONFIG FILES \n' + consoleLogLine);
const passDBConfigChanged: boolean = applyConfig('./conf/dovecot/ldap/passdb.conf', passDBConfig);
Expand All @@ -476,9 +477,6 @@ async function initializeSync(): Promise<void> {

console.log(consoleLogLine + '\n SYNCING ALL PERMISSIONS \n' + consoleLogLine);
await synchronizePermissionsWithActiveDirectory();

console.log(consoleLogLine + '\n CHECKING INACTIVE USERS \n' + consoleLogLine);
await synchronize();
}

/**
Expand Down
61 changes: 29 additions & 32 deletions src/localUserDatabase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,25 @@ import fs from 'fs';
import { ActiveDirectoryPermissions, ChangedUsers, ActiveUserSetting, LocalUserData } from './types';
import { sessionTime } from './index';

// Connection options for the DB
const dataSource = new DataSource({
type: 'sqlite',
database: './db/ldap-mailcow.sqlite3',
entities: [
Users,
],
synchronize: true,
});

let localUserRepository: Repository<Users>;
let dataSource: DataSource;

/**
* Initialize database connection. Setup database if it does not yet exist
*/
export async function initializeLocalUserDatabase(): Promise<void> {
if (!fs.existsSync('./db/ldap-mailcow.sqlite3'))
fs.writeFileSync('./db/ldap-mailcow.sqlite3', '');

dataSource = new DataSource({
type: 'sqlite',
database: './db/ldap-mailcow.sqlite3',
entities: [
Users,
],
synchronize: true,
});

dataSource.initialize().catch((error) => console.log(error));
localUserRepository = dataSource.getRepository(Users);
}
Expand All @@ -41,13 +42,13 @@ export async function getUncheckedLocalActiveUsers(): Promise<Users[]> {

/**
* Add a user to the DB
* @param email - mail entry in the database
* @param mail - mail entry in the database
* @param displayName
* @param active - whether user is active
*/
export async function addUserDB(email: string, displayName: string, active: ActiveUserSetting): Promise<void> {
export async function createLocalUser(mail: string, displayName: string, active: ActiveUserSetting): Promise<void> {
const user: Users = Object.assign(new Users(), {
email: email,
email: mail,
active: active,
displayName: displayName,
inactiveCount: 0,
Expand All @@ -68,9 +69,9 @@ export async function addUserDB(email: string, displayName: string, active: Acti

/**
* Get a user data from database
* @param email - mail from to be retrieved user
* @param mail - mail from to be retrieved user
*/
export async function getLocalUser(email: string): Promise<LocalUserData> {
export async function getLocalUser(mail: string): Promise<LocalUserData> {
const localUserData: LocalUserData = {
exists: false,
isActive: 0,
Expand All @@ -79,7 +80,7 @@ export async function getLocalUser(email: string): Promise<LocalUserData> {

const localUser: Users | null = await localUserRepository.findOne({
where: {
email: email,
email: mail,
},
});

Expand All @@ -98,38 +99,35 @@ export async function getLocalUser(email: string): Promise<LocalUserData> {

/**
* Change user activity status in the DB
* @param email - email of user
* @param mail - email of user
* @param active - activity of user
* @param inactiveCount - number of times user has been inactive
*/
export async function activityUserDB(email: string, active: ActiveUserSetting, inactiveCount: number): Promise<void> {
// Retrieve user with email
export async function updateLocalUserActivity(mail: string, active: ActiveUserSetting, inactiveCount: number): Promise<void> {
const user: Users = await localUserRepository.findOneOrFail({
where: {
email: email,
email: mail,
},
});
// Set new activity of user
user.active = active;
user.inactiveCount = inactiveCount;
await localUserRepository.update(user.email, user);
}

/**
* Update user's SOB
* @param email - email of user
* @param mail - email of user
* @param SOBEmail - acl to check
*/
export async function createSOBDB(email: string, SOBEmail: string): Promise<void> {
// Retrieve user with email
export async function createSOBDB(mail: string, SOBEmail: string): Promise<void> {
const user: Users = await localUserRepository.findOneOrFail({
where: {
email: email,
email: mail,
},
});

// Check if permissions for ACL are set
const SOB = !user.newMailPermSOB ? [] : user.newMailPermSOB.split(';');
const SOB: string[] = !user.newMailPermSOB ? [] : user.newMailPermSOB.split(';');

// Check if sob mail is in list (it should not be, but checking does not hurt)
if (SOB.indexOf(SOBEmail) === -1) {
Expand All @@ -139,14 +137,13 @@ export async function createSOBDB(email: string, SOBEmail: string): Promise<void
}
}

export async function getChangedSOBDB(): Promise<Users[]> {
// First, check all users that actually have changed
const users = await localUserRepository.find();
export async function getUpdateSOBLocalUsers(): Promise<Users[]> {
const users: Users[] = await localUserRepository.find();
const changedUsers : Users[] = [];

for (const user of users) {
if (user.newMailPermSOB != user.mailPermSOB) {
console.log(`SOB of ${user.email} changed from ${user.mailPermSOB} to ${user.newMailPermSOB}`);
console.log(`SOB of ${user.email} changed from ${user.mailPermSOB} to ${user.newMailPermSOB}.`);
user.mailPermSOB = user.newMailPermSOB;
changedUsers.push(user);
}
Expand Down Expand Up @@ -182,8 +179,8 @@ export async function updateLocalUserPermissions(mail: string, newUsers: string[

// Filter for users, also filter empty entries
const removedUsers : string[] = !user ? [] : user[permission].split(';');
changedUsers.newUsers = newUsers.filter(x => !removedUsers.includes(x) && x != '');
changedUsers.removedUsers = removedUsers.filter(x => !newUsers.includes(x) && x != '');
changedUsers.newUsers = newUsers.filter((innerUser: string) => !removedUsers.includes(innerUser) && innerUser != '');
changedUsers.removedUsers = removedUsers.filter((innerUser: string) => !newUsers.includes(innerUser) && innerUser != '');
user[permission] = newUsers.join(';');
await localUserRepository.update(user.email, user);

Expand Down
Loading

0 comments on commit 792fda6

Please sign in to comment.