Skip to content

Commit d236664

Browse files
Remove LDAP from codebase
* All searching will be moved to a seperate service to allow for more flexibility in how records are searched
1 parent e4e90ff commit d236664

2 files changed

Lines changed: 39 additions & 130 deletions

File tree

package.json

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@
1717
"devDependencies": {
1818
"@octokit/types": "^12.4.0",
1919
"@types/express": "^4.17.17",
20-
"@types/js-yaml": "^4.0.5",
21-
"@types/ldap-escape": "^2.0.0",
22-
"@types/ldapjs": "3.0.6",
20+
"@types/js-yaml": "^4.0.5",
2321
"@types/node": "^20.4.8",
2422
"@types/swagger-ui-express": "^4.1.3",
2523
"nodemon": "^3.0.2",
@@ -34,9 +32,7 @@
3432
"axios-retry": "^4.0.0",
3533
"dotenv": "^16.0.3",
3634
"express": "^4.18.2",
37-
"js-yaml": "^4.1.0",
38-
"ldap-escape": "^2.0.6",
39-
"ldapjs": "^3.0.1",
35+
"js-yaml": "^4.1.0",
4036
"nocache": "^4.0.0",
4137
"octokit": "3.1.2",
4238
"openapi-backend": "^5.9.1",

src/services/ldapClient.ts

Lines changed: 37 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,10 @@
11
import { Config } from "../config";
2-
import ldap from "ldapjs";
3-
import ldapEscape from "ldap-escape";
42
import axios from "axios";
53
import axiosRetry from "axios-retry";
6-
import { Log, LogError, LoggerToUse } from "../logging";
4+
import { Log, LoggerToUse } from "../logging";
75
import { redisClient } from "../app";
86

97
const config = Config()
10-
11-
let client: ldap.Client;
12-
13-
if (!process.env.SOURCE_PROXY) {
14-
client = ldap.createClient({
15-
url: [config.LDAP.Server]
16-
});
17-
18-
client.bind(config.LDAP.User, config.LDAP.Password, (err) => {
19-
if (err) {
20-
console.log("Failed to connect to LDAP Server");
21-
LogError(JSON.stringify(err) as string);
22-
}
23-
24-
console.log("Connected to LDAP Server");
25-
});
26-
}
27-
else {
28-
Log("Group Proxy is set. LDAP will not be configured for this instance.")
29-
}
30-
31-
function SearchAsync(groupName: string): Promise<ldap.SearchCallbackResponse> {
32-
const component = ldapEscape.filter`${groupName}`;
33-
const ldapSearchString = `(&(objectCategory=user)(memberOf=CN=${component},CN=Users,${config.LDAP.GroupBaseDN}))`
34-
35-
const opts: ldap.SearchOptions = {
36-
filter: ldapSearchString,
37-
scope: "sub",
38-
// paged:true
39-
attributes: ['dn', 'cn', 'userPrincipalName'],
40-
// paged: {
41-
// pageSize: 250,
42-
// pagePause: true
43-
// }
44-
};
45-
46-
return new Promise((resolve, reject) => {
47-
client.search(config.LDAP.GroupBaseDN, opts, (err, res) => {
48-
if (err) {
49-
LogError(`Error searching for ${component}: ${JSON.stringify(err)}`)
50-
return reject(err);
51-
}
52-
53-
return resolve(res);
54-
});
55-
})
56-
}
57-
588
export interface Entry {
599
cn: string,
6010
userPrincipalName: string
@@ -71,56 +21,6 @@ export type SearchAllSucceeded = {
7121

7222
export type SearchAllResponse = Promise<SearchAllFailed | SearchAllSucceeded>
7323

74-
async function SearchAllAsyncNoExceptionHandling(groupName: string): SearchAllResponse {
75-
// TODO: implement paging somehow!!
76-
const response = await SearchAsync(groupName);
77-
78-
const entries: Entry[] = [];
79-
80-
type rawEntry = {
81-
pojo: {
82-
attributes: {
83-
type:string,
84-
values:string[]
85-
}[]
86-
}
87-
}
88-
89-
return new Promise((resolve, reject) => {
90-
response.on('searchEntry', (entry: rawEntry) => {
91-
const attributes = entry.pojo.attributes.map((a) => {
92-
return [
93-
a.type,
94-
a.values[0]
95-
]
96-
})
97-
entries.push(Object.fromEntries(attributes) as Entry);
98-
});
99-
100-
type result = {
101-
status:number
102-
}
103-
104-
response.on('end', (result: result) => {
105-
Log(`Search Ended for Group '${groupName}' with result '${JSON.stringify(result)}'`)
106-
107-
if (result == null || result == undefined || result.status !== 0) {
108-
return reject(result.status);
109-
}
110-
111-
return resolve({
112-
entries: entries,
113-
Succeeded: true
114-
});
115-
});
116-
117-
response.on('error', (err: unknown) => {
118-
LogError(`Search Errored for Group '${groupName}': ${JSON.stringify(err)}`);
119-
return reject();
120-
});
121-
});
122-
}
123-
12424
export async function SearchAllAsync(groupName: string): SearchAllResponse {
12525
const cacheKey = `sot-group:${groupName}`;
12626

@@ -138,7 +38,8 @@ export async function SearchAllAsync(groupName: string): SearchAllResponse {
13838
return JSON.parse(result) as SearchAllResponse
13939
}
14040

141-
const actualResult = await PrivateSearchAllAsync(groupName);
41+
// Make API call here
42+
const actualResult = await ForwardSearch(groupName);
14243

14344
// Slightly complex for caching logic, but we don't want to cache useless results
14445
if (actualResult.Succeeded && actualResult.entries.length > 0) {
@@ -150,28 +51,11 @@ export async function SearchAllAsync(groupName: string): SearchAllResponse {
15051
return actualResult;
15152
}
15253

153-
async function PrivateSearchAllAsync(groupName: string): SearchAllResponse {
154-
try {
155-
if (process.env.SOURCE_PROXY) {
156-
return await ForwardSearch(groupName);
157-
}
158-
159-
return await SearchAllAsyncNoExceptionHandling(groupName);
160-
}
161-
catch (ex: unknown) {
162-
Log(JSON.stringify(ex));
163-
164-
return {
165-
Succeeded: false
166-
}
167-
}
168-
}
169-
17054
// TODO: do not directly use axios.create from within a function like this
17155
// it will cause a new client to be made per request.
17256
const httpClient = axios.create();
17357
axiosRetry(httpClient, {
174-
retries: 5,
58+
retries: 2,
17559
retryDelay: (retryCount) => {
17660
Log(`Retry attempt: ${retryCount}`);
17761
return retryCount * 2000;
@@ -188,15 +72,29 @@ axiosRetry(httpClient, {
18872
async function ForwardSearch(groupName: string): SearchAllResponse {
18973
Log(`Forwarding request to '${process.env.SOURCE_PROXY}'`);
19074

191-
const requestUrl = `${process.env.SOURCE_PROXY}/api/get-source-team?teamName=${groupName}`;
75+
const requestUrl = `${process.env.SOURCE_PROXY}/search/${groupName}`;
19276

19377
Log(`Retrieving group (${groupName}) information from '${requestUrl}'`);
19478
try {
195-
const result = await httpClient.get(requestUrl);
196-
Log(`Results for ${groupName}: ${result}`);
79+
const httpResponse = await httpClient.get(requestUrl);
80+
Log(`Results for ${groupName}: ${JSON.stringify(httpResponse.data)}`);
81+
82+
if(httpResponse.status < 200 || httpResponse.status > 299) {
83+
return {
84+
Succeeded: false
85+
}
86+
}
87+
88+
const response = httpResponse.data as SuccessResponse;
89+
19790
return {
19891
Succeeded: true,
199-
...result.data
92+
entries: response.users.map(u => {
93+
return {
94+
cn: u.username,
95+
userPrincipalName: u.email
96+
}
97+
})
20098
}
20199
}
202100
catch (e) {
@@ -207,3 +105,18 @@ async function ForwardSearch(groupName: string): SearchAllResponse {
207105
Succeeded: false
208106
}
209107
}
108+
109+
export interface User {
110+
username: string
111+
email: string
112+
}
113+
114+
export interface SuccessResponse {
115+
users: User[]
116+
}
117+
118+
export interface FailedResponse {
119+
Message: string
120+
}
121+
122+
export type SearchResponse = SuccessResponse | FailedResponse

0 commit comments

Comments
 (0)