ldapInterface.ts 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. const ldapjs = require("ldapjs");
  2. import ConfigManager, { LDAPAttribute, LDAPCategory } from "./ConfigLoader";
  3. import { LDAPSchemaAttribute, LDAPSchemaObjectClass } from './LDAPSchema';
  4. import LDAPEntry from "./LDAPEntry";
  5. import LDAPTree from "./LDAPTree";
  6. interface ILDAPManager {
  7. Release(): Promise<void>;
  8. /**
  9. * Try to login as user to check password
  10. * @param dn
  11. * @param password
  12. */
  13. TryBind(dn: string, password: string): Promise<boolean>;
  14. CheckLoginExists(user: string): Promise<string>;
  15. ListEntries(category: LDAPCategory): Promise<LDAPEntry[]>;
  16. /** @return all dn */
  17. GetTree(): Promise<LDAPTree>;
  18. GetEntry(dn: string): Promise<Map<string, string[]>>;
  19. Remove(dn: string): Promise<void>;
  20. GetSchema(): Promise<Map<string, LDAPSchemaObjectClass>>;
  21. }
  22. class LDAPManager implements ILDAPManager {
  23. protected cli: any;
  24. public constructor(cli: any) {
  25. this.cli = cli;
  26. }
  27. public static Create(): Promise<LDAPManager> {
  28. return new Promise((ok, ko) => {
  29. let config = ConfigManager.GetInstance();
  30. let cli = ldapjs.createClient({
  31. url: config.GetLDAPUrls()
  32. });
  33. cli.on('error', (err: any) => {
  34. console.error("LDAP general error: ", err);
  35. ko(err);
  36. });
  37. cli.bind(config.GetBindDn(), config.GetBindPassword(), (err: any) => {
  38. if (err) {
  39. console.error("LDAP bind error: ", err);
  40. ko(err);
  41. } else {
  42. ok(new LDAPManager(cli));
  43. }
  44. });
  45. });
  46. }
  47. public TryBind(userDn: string, password: string): Promise<boolean> {
  48. return new Promise((ok, ko) => {
  49. let cli = ldapjs.createClient({
  50. url: ConfigManager.GetInstance().GetLDAPUrls()
  51. });
  52. cli.on('error', (err: any) => {
  53. console.error("LDAP general error: ", err);
  54. ko(err);
  55. });
  56. cli.bind(userDn, password, (err: any) => {
  57. if (err) {
  58. ok(false);
  59. } else {
  60. ok(true);
  61. }
  62. });
  63. });
  64. }
  65. private Search(base: string, scope: string, filter: string|undefined =undefined, attributes: string[]|undefined =undefined): Promise<Map<string, Map<string, string[]>>> {
  66. return new Promise((ok, ko) => {
  67. this.cli.search(base, { scope: scope, filter: filter, attributes: attributes || ['*', 'memberof'], paged: false }, (err: any, res: any) => {
  68. if (err) {
  69. console.error("Search error: ", { base: base, scope: scope, filter: filter, attributes: attributes });
  70. ko(err);
  71. return;
  72. }
  73. let result = new Map<string, any>();
  74. let error = false;
  75. res.on('searchEntry', (i: any) => {
  76. if (error) return;
  77. let LDAPEntry = new Map<string, string[]>();
  78. for (let attr in i.object) {
  79. let value = i.object[attr];
  80. if (Array.isArray(value)) {
  81. let arr = new Array();
  82. for (let j of value)
  83. arr.push(j);
  84. value = arr;
  85. } else {
  86. let arr = new Array();
  87. arr.push(value);
  88. value = arr;
  89. }
  90. LDAPEntry.set(attr, value);
  91. }
  92. result.set(i.dn, LDAPEntry);
  93. });
  94. res.on('error', (err: any) => {
  95. error = true;
  96. console.error("Search error: ", { base: base, scope: scope, filter: filter, attributes: attributes });
  97. ko(err);
  98. });
  99. res.on('end', () => {
  100. if (error) return;
  101. ok(result);
  102. });
  103. });
  104. });
  105. }
  106. public GetEntry(dn: string): Promise<Map<string, string[]>> {
  107. return new Promise((ok, ko) => {
  108. this.Search(dn, "sub").then(result => {
  109. for (let [_, val] of result) {
  110. ok(val);
  111. return;
  112. }
  113. }).catch(err => {
  114. ko(err);
  115. });
  116. });
  117. }
  118. public CheckLoginExists(user: string): Promise<string> {
  119. return new Promise((ok, ko) => {
  120. let config = ConfigManager.GetInstance();
  121. return this.Search(config.GetLoginBase(), config.GetLoginScope(), config.GetLoginFilter(user), []).then(result => {
  122. if (!result || result.size !== 1)
  123. ko();
  124. for (let [addr, _] of result) {
  125. ok(addr);
  126. }
  127. });
  128. });
  129. }
  130. public GetTree(): Promise<LDAPTree> {
  131. return new Promise(ok => {
  132. const rootDn: string = ConfigManager.GetInstance().GetLDAPRoot();
  133. this.Search(rootDn, "sub", undefined, ["dn", "ObjectClass"]).then(searchDns => {
  134. let result = LDAPTree.CreateRoot();
  135. searchDns.forEach((val, dn) => {
  136. let classes = val.get("objectClass");
  137. classes && result.push(dn, classes);
  138. });
  139. ok(result.Compress());
  140. });
  141. });
  142. }
  143. public ListEntries(category: LDAPCategory): Promise<LDAPEntry[]> {
  144. return new Promise((ok, _) => {
  145. this.Search(category.GetBaseDn(), category.GetScope(), category.GetFilter() || undefined, category.GetAttributes().map(i => i.mapped)).then(result => {
  146. let userArray = new Array();
  147. for (let [addr, val] of result)
  148. userArray.push(new LDAPEntry(category.GetAttributes(), addr, val));
  149. ok(userArray);
  150. });
  151. });
  152. }
  153. public Release():Promise<void> {
  154. return new Promise(ok => {
  155. this.cli.unbind((err: any) => {
  156. if (err)
  157. console.error("LDAP unbind error: ", err);
  158. ok();
  159. });
  160. });
  161. }
  162. public Remove(dn: string): Promise<void> {
  163. return new Promise((ok, ko) => {
  164. this.cli.del(dn, (err: any) => {
  165. if (err)
  166. ko(err["message"]);
  167. else
  168. ok();
  169. });
  170. });
  171. }
  172. public GetSchema(): Promise<Map<string, LDAPSchemaObjectClass>> {
  173. return new Promise((ok, ko) => {
  174. const rootDn = ConfigManager.GetInstance().GetLDAPRoot();
  175. let subschema: string;
  176. this.Search(rootDn, "base", undefined, ["subschemaSubentry"]).then(result => {
  177. const baseItem = result?.get(rootDn)?.get("subschemaSubentry");
  178. const _subschema = baseItem && baseItem.length ? baseItem[0] : null;
  179. if (!_subschema) {
  180. console.error("Cannot find schema for base " + rootDn);
  181. ko("Schema not found");
  182. return;
  183. }
  184. subschema = _subschema;
  185. return this.Search(subschema, "base", undefined, ["attributeTypes", "objectClasses"]);
  186. }).then(result => {
  187. const attributesArr = result?.get(subschema)?.get("attributeTypes");
  188. const classesArr = result?.get(subschema)?.get("objectClasses");
  189. if (!attributesArr || !classesArr) {
  190. console.error("Cannot find schema definition for " + rootDn);
  191. ko("Schema definition not found");
  192. return;
  193. }
  194. let attributes: Map<string, LDAPSchemaAttribute> = new Map();
  195. let schemas: Map<string, LDAPSchemaObjectClass> = new Map();
  196. for (let i of attributesArr) {
  197. let attr = new LDAPSchemaAttribute(i);
  198. attributes.set(attr.GetName(), attr);
  199. }
  200. for (let i of classesArr) {
  201. let schema = new LDAPSchemaObjectClass(i, attributes);
  202. schemas.set(schema.GetName(), schema);
  203. }
  204. for (let [_, schema] of schemas)
  205. schema.Consolidate(schemas, attributes);
  206. ok(schemas);
  207. }).catch(err => {
  208. console.error("Failed to retreive schema: ", err);
  209. ko(err);
  210. });
  211. });
  212. }
  213. }
  214. export default class LDAPFactory {
  215. private _instance: ILDAPManager|null = null;
  216. public GetInstance(): Promise<ILDAPManager> {
  217. if (!this._instance) {
  218. return new Promise((ok, ko) => {
  219. LDAPManager.Create().then(inst => {
  220. this._instance = inst;
  221. ok(this._instance);
  222. });
  223. });
  224. }
  225. return Promise.resolve(this._instance);
  226. }
  227. public Release(): Promise<null> {
  228. if (this._instance)
  229. return this._instance.Release().then(() => this._instance = null);
  230. return Promise.resolve(null);
  231. }
  232. }
  233. export { ILDAPManager };