All files / src/auth/resolvers AdfsCredentials.ts

97.5% Statements 39/40
83.33% Branches 5/6
100% Functions 5/5
97.5% Lines 39/40
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 1081x 1x 1x 1x 1x 1x 1x   1x 1x         1x 1x 1x     1x   1x     6x 6x   6x             6x             6x           6x   6x 6x   6x 4x             2x   2x     2x 2x 2x 2x 2x   2x   2x                 2x   2x             2x 2x 2x   2x                        
import * as Promise from 'bluebird';
import * as request from 'request-promise';
import * as url from 'url';
import * as _ from 'lodash';
import * as fs from 'fs';
import * as path from 'path';
import * as cookie from 'cookie';
import { IncomingMessage } from 'http';
import * as util from 'util';
let xmldoc: any = require('xmldoc');
 
import { IAuthResolver } from './../IAuthResolver';
import { IAdfsUserCredentials } from './../IAuthOptions';
import { IAuthResponse } from './../IAuthResponse';
import { Cache } from './../../utils/Cache';
import * as consts from './../../Consts';
import { AdfsHelper } from './../../utils/AdfsHelper';
import { SamlAssertion } from './../../utils/SamlAssertion';
 
export class AdfsCredentials implements IAuthResolver {
 
  private static CookieCache: Cache = new Cache();
  private _authOptions: IAdfsUserCredentials;
 
  constructor(private _siteUrl: string, _authOptions: IAdfsUserCredentials) {
    this._authOptions = _.extend<{}, IAdfsUserCredentials>({}, _authOptions);
 
    this._authOptions.username = this._authOptions.username
      .replace(/&/g, '&amp;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&apos;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;');
 
    this._authOptions.password = this._authOptions.password
      .replace(/&/g, '&amp;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&apos;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;');
 
    Iif (this._authOptions.domain !== undefined) {
      this._authOptions.username = `${this._authOptions.domain}\\${this._authOptions.username}`;
    }
  }
 
  public getAuth(): Promise<IAuthResponse> {
    let siteUrlParsed: url.Url = url.parse(this._siteUrl);
 
    let cacheKey: string = util.format('%s@%s', siteUrlParsed.host, this._authOptions.username);
    let cachedCookie: string = AdfsCredentials.CookieCache.get<string>(cacheKey);
 
    if (cachedCookie) {
      return Promise.resolve({
        headers: {
          'Cookie': cachedCookie
        }
      });
    }
 
    return AdfsHelper.getSamlAssertion(this._siteUrl, this._authOptions)
      .then(data => {
        return this.postTokenData(data);
      })
      .then(data => {
        let adfsCookie: string = this._authOptions.adfsCookie || consts.FedAuth;
        let notAfter: number = new Date(data[0]).getTime();
        let expiresIn: number = parseInt(((notAfter - new Date().getTime()) / 1000).toString(), 10);
        let response: IncomingMessage = data[1];
        let authCookie: string = adfsCookie + '=' + cookie.parse(response.headers['set-cookie'][0])[adfsCookie];
 
        AdfsCredentials.CookieCache.set(cacheKey, authCookie, expiresIn);
 
        return {
          headers: {
            'Cookie': authCookie
          }
        };
      });
  }
 
  private postTokenData(samlAssertion: SamlAssertion): Promise<[string, any]> {
    let tokenPostTemplate: Buffer = fs.readFileSync(path.join(__dirname, '..', '..', '..', 'templates', 'adfs_saml_token.tmpl'));
 
    let result: string = _.template(tokenPostTemplate.toString())({
      created: samlAssertion.notBefore,
      expires: samlAssertion.notAfter,
      relyingParty: this._authOptions.relyingParty,
      token: samlAssertion.value
    });
 
    let tokenXmlDoc: any = new xmldoc.XmlDocument(result);
    let siteUrlParsed: url.Url = url.parse(this._siteUrl);
    let rootSiteUrl = `${siteUrlParsed.protocol}//${siteUrlParsed.host}`;
 
    return Promise.all([samlAssertion.notAfter, request.post(`${rootSiteUrl}/_trust/`, {
      form: {
        'wa': 'wsignin1.0',
        'wctx': `${rootSiteUrl}/_layouts/Authenticate.aspx?Source=%2F`,
        'wresult': tokenXmlDoc.toString({ compressed: true })
      },
      resolveWithFullResponse: true,
      simple: false,
      strictSSL: false
    })]);
  }
}