Skip to content

Commit

Permalink
Merge d4c5d0c into 5664130
Browse files Browse the repository at this point in the history
  • Loading branch information
tngan committed Sep 25, 2018
2 parents 5664130 + d4c5d0c commit 2a9f469
Show file tree
Hide file tree
Showing 22 changed files with 1,180 additions and 943 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ branches:
- master
- /^.*-alpha$/
- /^.*-beta$/
- /^.*-rc*$/

after_success: npm run coverage
2 changes: 2 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ export { default as SamlLib } from './src/libsaml';
// roadmap
// new name convention in version >= 3.0
import * as Constants from './src/urn';
import * as Extractor from './src/extractor';

export {
Constants,
Extractor
};
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "samlify",
"version": "2.4.0-rc2",
"version": "2.4.0-rc3",
"description": "High-level API for Single Sign On (SAML 2.0)",
"main": "build/index.js",
"keywords": [
Expand All @@ -17,8 +17,8 @@
"lint": "tslint -p .",
"lint:fix": "tslint -p . --fix",
"pretest": "make pretest",
"test": "npm run build && nyc ava --verbose build/test",
"test:pure": "nyc ava --verbose build/test",
"test": "npm run build && NODE_ENV=test nyc ava --verbose build/test",
"test:pure": "NODE_ENV=test nyc ava --verbose build/test",
"coverage": "nyc report --reporter=text-lcov | coveralls",
"hooks:postinstall": "ln -sf $PWD/.pre-commit.sh $PWD/.git/hooks/pre-commit"
},
Expand Down Expand Up @@ -58,4 +58,4 @@
"nyc": "^11.9.0",
"tslint": "5.4.2"
}
}
}
2 changes: 1 addition & 1 deletion schemas/xmldsig-core-schema.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@
<complexType name="X509IssuerSerialType">
<sequence>
<element name="X509IssuerName" type="string"/>
<element name="X509SerialNumber" type="integer"/>
<element name="X509SerialNumber" type="string"/>
</sequence>
</complexType>

Expand Down
16 changes: 7 additions & 9 deletions src/binding-post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* @desc Binding-level API, declare the functions using POST binding
*/

import { wording, tags, namespace } from './urn';
import { wording, tags, namespace, StatusCode } from './urn';
import { BindingContext } from './entity';
import libsaml from './libsaml';
import utility from './utility';
Expand Down Expand Up @@ -102,14 +102,14 @@ async function base64LoginResponse(requestInfo: any = {}, entity: any, user: any
Issuer: metadata.idp.getEntityID(),
IssueInstant: now,
AssertionConsumerServiceURL: acl,
StatusCode: namespace.statusCode.success,
StatusCode: StatusCode.Success,
// can be customized
ConditionsNotBefore: now,
ConditionsNotOnOrAfter: fiveMinutesLater,
SubjectConfirmationDataNotOnOrAfter: fiveMinutesLater,
NameIDFormat: namespace.format[idpSetting.logoutNameIDFormat] || namespace.format.emailAddress,
NameID: user.email || '',
InResponseTo: get(requestInfo, 'extract.authnrequest.id') || '',
InResponseTo: get(requestInfo, 'extract.request.id') || '',
AuthnStatement: '',
AttributeStatement: '',
};
Expand All @@ -118,7 +118,7 @@ async function base64LoginResponse(requestInfo: any = {}, entity: any, user: any
rawSamlResponse = get<BindingContext, keyof BindingContext>(template, 'context');
} else {
if (requestInfo !== null) {
tvalue.InResponseTo = requestInfo.extract.authnrequest.id;
tvalue.InResponseTo = requestInfo.extract.request.id;
}
rawSamlResponse = libsaml.replaceTagsByValue(libsaml.defaultLoginResponseTemplate.context, tvalue);
}
Expand All @@ -130,7 +130,6 @@ async function base64LoginResponse(requestInfo: any = {}, entity: any, user: any
signingCert: metadata.idp.getX509Certificate('signing'),
isBase64Output: false,
};

// SAML response must be signed sign message first, then encrypt
if (!encryptThenSign && (spSetting.wantMessageSigned || !metadata.sp.isWantAssertionsSigned())) {
rawSamlResponse = libsaml.constructSAMLSignature({
Expand All @@ -143,7 +142,6 @@ async function base64LoginResponse(requestInfo: any = {}, entity: any, user: any
},
});
}

// step: sign assertion ? -> encrypted ? -> sign message ?
if (metadata.sp.isWantAssertionsSigned()) {
rawSamlResponse = libsaml.constructSAMLSignature({
Expand Down Expand Up @@ -269,10 +267,10 @@ function base64LogoutResponse(requestInfo: any, entity: any, customTagReplacemen
EntityID: metadata.init.getEntityID(),
Issuer: metadata.init.getEntityID(),
IssueInstant: new Date().toISOString(),
StatusCode: namespace.statusCode.success,
StatusCode: StatusCode.Success,
};
if (requestInfo && requestInfo.extract && requestInfo.extract.logoutrequest) {
tvalue.InResponseTo = requestInfo.extract.logoutrequest.id;
if (requestInfo && requestInfo.extract && requestInfo.extract.request) {
tvalue.InResponseTo = requestInfo.extract.request.id;
}
rawSamlResponse = libsaml.replaceTagsByValue(libsaml.defaultLogoutResponseTemplate.context, tvalue);
}
Expand Down
44 changes: 16 additions & 28 deletions src/entity-idp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,11 @@ import {
IdentityProviderSettings,
} from './types';
import libsaml from './libsaml';
import utility from './utility';
import { wording, namespace, tags } from './urn';
import redirectBinding from './binding-redirect';
import { namespace } from './urn';
import postBinding from './binding-post';
import { isString } from 'lodash';
import * as xml from 'xml';
import { flow } from './flow';

const bindDict = wording.binding;
const xmlTag = tags.xmlTag;
const metaWord = wording.metadata;

/**
* Identity prvider can be configured using either metadata importing or idpSetting
Expand All @@ -33,6 +28,7 @@ export default function(props: IdentityProviderSettings) {
* Identity prvider can be configured using either metadata importing or idpSetting
*/
export class IdentityProvider extends Entity {

entityMeta: IdentityProviderMetadata;

constructor(idpSetting: IdentityProviderSettings) {
Expand Down Expand Up @@ -77,46 +73,38 @@ export class IdentityProvider extends Entity {
customTagReplacement?: (...args: any[]) => any,
encryptThenSign?: boolean,
) {
const protocol = namespace.binding[binding] || namespace.binding.redirect;
const protocol = namespace.binding[binding];
// can only support post binding for login response
if (protocol === namespace.binding.post) {
const context = await postBinding.base64LoginResponse(requestInfo, {
idp: this,
sp,
}, user, customTagReplacement, encryptThenSign);
// xmlenc is using async process
return {
return {
...context,
entityEndpoint: (sp.entityMeta as ServiceProviderMetadata).getAssertionConsumerService(binding),
type: 'SAMLResponse',
type: 'SAMLResponse'
};
}

// Will support artifact in the next release
throw new Error('this binding is not supported');
throw new Error('ERR_CREATE_RESPONSE_UNDEFINED_BINDING');
}

/**
* Validation of the parsed URL parameters
* @param sp ServiceProvider instance
* @param binding Protocol binding
* @param req Request
* @param req RequesmessageSigningOrderst
*/
public parseLoginRequest(sp: ServiceProvider, binding: string, req: ESamlHttpRequest) {
return this.genericParser({
parserFormat: ['AuthnContextClassRef', 'Issuer', {
localName: 'Signature',
extractEntireBody: true,
}, {
localName: 'AuthnRequest',
attributes: ['ID'],
}, {
localName: 'NameIDPolicy',
attributes: ['Format', 'AllowCreate'],
}],
const self = this;
return flow({
from: sp,
checkSignature: this.entityMeta.isWantAuthnRequestsSigned(),
self: self,
checkSignature: self.entityMeta.isWantAuthnRequestsSigned(),
parserType: 'SAMLRequest',
type: 'login',
}, binding, req);
binding: binding,
request: req
});
}
}
50 changes: 14 additions & 36 deletions src/entity-sp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,21 @@
* @author tngan
* @desc Declares the actions taken by service provider
*/
import Entity, { BindingContext, PostBindingContext, ESamlHttpRequest, ParseResult } from './entity';
import Entity, {
BindingContext,
PostBindingContext,
ESamlHttpRequest,
} from './entity';
import {
IdentityProviderConstructor as IdentityProvider,
ServiceProviderMetadata,
ServiceProviderSettings,
} from './types';
import libsaml from './libsaml';
import utility from './utility';
import { wording, namespace, tags } from './urn';
import { namespace } from './urn';
import redirectBinding from './binding-redirect';
import postBinding from './binding-post';
import * as xml from 'xml';

const bindDict = wording.binding;
const xmlTag = tags.xmlTag;
const metaWord = wording.metadata;
import { flow } from './flow';

/*
* @desc interface function
Expand All @@ -30,15 +29,13 @@ export default function(props: ServiceProviderSettings) {
/**
* @desc Service provider can be configured using either metadata importing or spSetting
* @param {object} spSetting
* @param {string} meta
*/
export class ServiceProvider extends Entity {
entityMeta: ServiceProviderMetadata;

/**
* @desc Inherited from Entity
* @param {object} spSetting setting of service provider
* @param {string} meta metadata
*/
constructor(spSetting: ServiceProviderSettings) {
const entitySetting = Object.assign({
Expand Down Expand Up @@ -89,36 +86,17 @@ export class ServiceProvider extends Entity {
* @param {string} binding protocol binding
* @param {request} req request
*/
public parseLoginResponse(idp, binding, req: ESamlHttpRequest) {
return this.genericParser({
parserFormat: [{
localName: 'StatusCode',
attributes: ['Value'],
}, {
localName: 'Conditions',
attributes: ['NotBefore', 'NotOnOrAfter'],
}, 'Audience', 'Issuer', 'NameID', {
localName: 'Signature',
extractEntireBody: true,
}, {
localName: {
tag: 'Attribute',
key: 'Name',
},
valueTag: 'AttributeValue',
}, {
localName: 'AuthnStatement',
attributes: ['SessionIndex'],
}, {
localName: 'Response',
attributes: ['InResponseTo'] },
],
public parseLoginResponse(idp, binding, request: ESamlHttpRequest) {
const self = this;
return flow({
from: idp,
self: self,
checkSignature: true, // saml response must have signature
supportBindings: ['post'],
parserType: 'SAMLResponse',
type: 'login',
}, binding, req);
binding: binding,
request: request
});
}

}

0 comments on commit 2a9f469

Please sign in to comment.