1
0
mirror of https://github.com/bitwarden/directory-connector synced 2025-12-10 13:23:18 +00:00

starttls: Support LDAP STARTTLS (#33)

* starttls: Support LDAP STARTTLS

* starttls: Re-roll to preserve old config
This commit is contained in:
Colin Campbell
2020-03-04 17:08:47 +01:00
committed by GitHub
parent f63fb3ffa0
commit 20bb5a4926
4 changed files with 107 additions and 53 deletions

View File

@@ -35,39 +35,54 @@
</div>
<div class="form-group">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="ssl" [(ngModel)]="ldap.ssl" name="SSL">
<label class="form-check-label" for="ssl">{{'ldapSsl' | i18n}}</label>
<input class="form-check-input" type="checkbox" id="ldapEncrypted" [(ngModel)]="ldap.ssl" name="Encrypted">
<label class="form-check-label" for="ldapEncrypted">{{'ldapEncrypted' | i18n}}</label>
</div>
</div>
<div class="ml-4" *ngIf="ldap.ssl">
<p>{{'ldapSslUntrustedDesc' | i18n}}</p>
<div class="form-group">
<label for="sslCertPath">{{'ldapSslCert' | i18n}}</label>
<input type="file" class="form-control-file mb-2" id="sslCertPath_file"
(change)="setSslPath('sslCertPath')">
<input type="text" class="form-control" id="sslCertPath" name="SSLCertPath"
[(ngModel)]="ldap.sslCertPath">
<div class="form-radio">
<input class="form-radio-input" type="radio" [value]=true id="starttls" [(ngModel)]="ldap.starttls" name="StartTls">
<label class="form-radio-label" for="starttls">{{'ldapTls' | i18n}}</label>
</div>
<div class="form-radio">
<input class="form-radio-input" type="radio" [value]=false id="ssl" [(ngModel)]="ldap.starttls" name="SSL">
<label class="form-radio-label" for="ssl">{{'ldapSsl' | i18n}}</label>
</div>
</div>
<div class="form-group">
<label for="sslKeyPath">{{'ldapSslKey' | i18n}}</label>
<input type="file" class="form-control-file mb-2" id="sslKeyPath_file"
(change)="setSslPath('sslKeyPath')">
<input type="text" class="form-control" id="sslKeyPath" name="SSLKeyPath"
[(ngModel)]="ldap.sslKeyPath">
<div class="ml-4" *ngIf="ldap.starttls">
<p>{{'ldapTlsUntrustedDesc' | i18n}}</p>
<div class="form-group">
<label for="tlsCaPath">{{'ldapTlsCa' | i18n}}</label>
<input type="file" class="form-control-file mb-2" id="tlsCaPath_file"
(change)="setSslPath('tlsCaPath')">
<input type="text" class="form-control" id="tlsCaPath" name="TLSCaPath" [(ngModel)]="ldap.tlsCaPath">
</div>
</div>
<div class="form-group">
<label for="sslCaPath">{{'ldapSslCa' | i18n}}</label>
<input type="file" class="form-control-file mb-2" id="sslCaPath_file"
(change)="setSslPath('sslCaPath')">
<input type="text" class="form-control" id="sslCaPath" name="SSLCaPath"
[(ngModel)]="ldap.sslCaPath">
<div class="ml-4" *ngIf="!ldap.starttls">
<p>{{'ldapSslUntrustedDesc' | i18n}}</p>
<div class="form-group">
<label for="sslCertPath">{{'ldapSslCert' | i18n}}</label>
<input type="file" class="form-control-file mb-2" id="sslCertPath_file"
(change)="setSslPath('sslCertPath')">
<input type="text" class="form-control" id="sslCertPath" name="SSLCertPath" [(ngModel)]="ldap.sslCertPath">
</div>
<div class="form-group">
<label for="sslKeyPath">{{'ldapSslKey' | i18n}}</label>
<input type="file" class="form-control-file mb-2" id="sslKeyPath_file" (change)="setSslPath('sslKeyPath')">
<input type="text" class="form-control" id="sslKeyPath" name="SSLKeyPath" [(ngModel)]="ldap.sslKeyPath">
</div>
<div class="form-group">
<label for="sslCaPath">{{'ldapSslCa' | i18n}}</label>
<input type="file" class="form-control-file mb-2" id="sslCaPath_file" (change)="setSslPath('sslCaPath')">
<input type="text" class="form-control" id="sslCaPath" name="SSLCaPath" [(ngModel)]="ldap.sslCaPath">
</div>
</div>
<div class="form-group">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="sslAllowUnauthorized"
[(ngModel)]="ldap.sslAllowUnauthorized" name="SSLAllowUnauthorized">
<label class="form-check-label"
for="sslAllowUnauthorized">{{'ldapSslAllowUnauthorized' | i18n}}</label>
<input class="form-check-input" type="checkbox" id="certDoNotVerify"
[(ngModel)]="ldap.sslAllowUnauthorized" name="CertDoNoVerify">
<label class="form-check-label" for="certDoNotVerify">{{'ldapCertDoNotVerify' | i18n}}</label>
</div>
</div>
</div>

View File

@@ -414,8 +414,20 @@
"sync": {
"message": "Sync"
},
"ldapEncrypted": {
"message": "This server uses an encrypted connection"
},
"ldapTls": {
"message": "Use TLS (STARTTLS)"
},
"ldapTlsCa": {
"message": "Certificate CA Chain (PEM)"
},
"ldapSsl": {
"message": "This server uses SSL (LDAPS)"
"message": "Use SSL (LDAPS)"
},
"ldapTlsUntrustedDesc": {
"message": "If your LDAP server uses a self-signed certificate for STARTTLS, you can configure certificate options below."
},
"ldapSslUntrustedDesc": {
"message": "If your LDAPS server uses an untrusted certificate you can configure certificate options below."
@@ -429,8 +441,8 @@
"ldapSslKey": {
"message": "Certificate Private Key (PEM)"
},
"ldapSslAllowUnauthorized": {
"message": "Allow untrusted SSL connections (not recommended)."
"ldapCertDoNotVerify": {
"message": "Do not verify server certificates (not recommended)."
},
"ldapAd": {
"message": "This server uses Active Directory"

View File

@@ -1,5 +1,7 @@
export class LdapConfiguration {
ssl = false;
starttls = true;
tlsCaPath: string;
sslAllowUnauthorized = false;
sslCertPath: string;
sslKeyPath: string;

View File

@@ -324,32 +324,41 @@ export class LdapDirectoryService implements DirectoryService {
reject(this.i18nService.t('dirConfigIncomplete'));
return;
}
const url = 'ldap' + (this.dirConfig.ssl ? 's' : '') + '://' + this.dirConfig.hostname +
const protocol = 'ldap' + (this.dirConfig.ssl && !this.dirConfig.starttls ? 's' : '');
const url = protocol + '://' + this.dirConfig.hostname +
':' + this.dirConfig.port;
const options: ldap.ClientOptions = {
url: url.trim().toLowerCase(),
};
const tlsOptions: any = {};
if (this.dirConfig.ssl) {
const tlsOptions: any = {};
if (this.dirConfig.sslAllowUnauthorized != null) {
if (!this.dirConfig.starttls) {
if (this.dirConfig.sslCaPath != null && this.dirConfig.sslCaPath !== '' &&
fs.existsSync(this.dirConfig.sslCaPath)) {
tlsOptions.ca = [fs.readFileSync(this.dirConfig.sslCaPath)];
}
if (this.dirConfig.sslCertPath != null && this.dirConfig.sslCertPath !== '' &&
fs.existsSync(this.dirConfig.sslCertPath)) {
tlsOptions.cert = fs.readFileSync(this.dirConfig.sslCertPath);
}
if (this.dirConfig.sslKeyPath != null && this.dirConfig.sslKeyPath !== '' &&
fs.existsSync(this.dirConfig.sslKeyPath)) {
tlsOptions.key = fs.readFileSync(this.dirConfig.sslKeyPath);
}
} else {
if (this.dirConfig.tlsCaPath != null && this.dirConfig.tlsCaPath !== '' &&
fs.existsSync(this.dirConfig.tlsCaPath)) {
tlsOptions.ca = [fs.readFileSync(this.dirConfig.tlsCaPath)];
}
}
if (this.dirConfig.sslAllowUnauthorized) {
tlsOptions.rejectUnauthorized = !this.dirConfig.sslAllowUnauthorized;
}
if (this.dirConfig.sslCaPath != null && this.dirConfig.sslCaPath !== '' &&
fs.existsSync(this.dirConfig.sslCaPath)) {
tlsOptions.ca = [fs.readFileSync(this.dirConfig.sslCaPath)];
}
if (this.dirConfig.sslCertPath != null && this.dirConfig.sslCertPath !== '' &&
fs.existsSync(this.dirConfig.sslCertPath)) {
tlsOptions.cert = fs.readFileSync(this.dirConfig.sslCertPath);
}
if (this.dirConfig.sslKeyPath != null && this.dirConfig.sslKeyPath !== '' &&
fs.existsSync(this.dirConfig.sslKeyPath)) {
tlsOptions.key = fs.readFileSync(this.dirConfig.sslKeyPath);
}
if (Object.keys(tlsOptions).length > 0) {
options.tlsOptions = tlsOptions;
}
}
if (Object.keys(tlsOptions).length > 0) {
options.tlsOptions = tlsOptions;
}
this.client = ldap.createClient(options);
@@ -364,13 +373,29 @@ export class LdapDirectoryService implements DirectoryService {
return;
}
this.client.bind(user, pass, (err) => {
if (err != null) {
reject(err.message);
} else {
resolve();
}
});
if (this.dirConfig.starttls && this.dirConfig.ssl) {
this.client.starttls(options.tlsOptions, undefined, (err, res) => {
if (err != null) {
reject(err.message);
} else {
this.client.bind(user, pass, (err) => {
if (err != null) {
reject(err.message);
} else {
resolve();
}
});
}
});
} else {
this.client.bind(user, pass, (err) => {
if (err != null) {
reject(err.message);
} else {
resolve();
}
});
}
});
}