-
-
Notifications
You must be signed in to change notification settings - Fork 217
/
entity-idp.ts
145 lines (135 loc) · 5.24 KB
/
entity-idp.ts
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/**
* @file entity-idp.ts
* @author tngan
* @desc Declares the actions taken by identity provider
*/
import Entity, { ESamlHttpRequest } from './entity';
import {
ServiceProviderConstructor as ServiceProvider,
ServiceProviderMetadata,
IdentityProviderMetadata,
IdentityProviderSettings,
} from './types';
import libsaml from './libsaml';
import { namespace } from './urn';
import postBinding from './binding-post';
import redirectBinding from './binding-redirect';
import simpleSignBinding from './binding-simplesign';
import { flow, FlowResult } from './flow';
import { isString } from './utility';
import { BindingContext } from './entity';
/**
* Identity prvider can be configured using either metadata importing or idpSetting
*/
export default function(props: IdentityProviderSettings) {
return new IdentityProvider(props);
}
/**
* Identity prvider can be configured using either metadata importing or idpSetting
*/
export class IdentityProvider extends Entity {
entityMeta: IdentityProviderMetadata;
constructor(idpSetting: IdentityProviderSettings) {
const defaultIdpEntitySetting = {
wantAuthnRequestsSigned: false,
tagPrefix: {
encryptedAssertion: 'saml',
},
};
const entitySetting = Object.assign(defaultIdpEntitySetting, idpSetting);
// build attribute part
if (idpSetting.loginResponseTemplate) {
if (isString(idpSetting.loginResponseTemplate.context) && Array.isArray(idpSetting.loginResponseTemplate.attributes)) {
let attributeStatementTemplate;
let attributeTemplate;
if (!idpSetting.loginResponseTemplate.additionalTemplates || !idpSetting.loginResponseTemplate.additionalTemplates!.attributeStatementTemplate) {
attributeStatementTemplate = libsaml.defaultAttributeStatementTemplate;
} else {
attributeStatementTemplate = idpSetting.loginResponseTemplate.additionalTemplates!.attributeStatementTemplate!;
}
if (!idpSetting.loginResponseTemplate.additionalTemplates || !idpSetting.loginResponseTemplate.additionalTemplates!.attributeTemplate) {
attributeTemplate = libsaml.defaultAttributeTemplate;
} else {
attributeTemplate = idpSetting.loginResponseTemplate.additionalTemplates!.attributeTemplate!;
}
const replacement = {
AttributeStatement: libsaml.attributeStatementBuilder(idpSetting.loginResponseTemplate.attributes, attributeTemplate, attributeStatementTemplate),
};
entitySetting.loginResponseTemplate = {
...entitySetting.loginResponseTemplate,
context: libsaml.replaceTagsByValue(entitySetting.loginResponseTemplate!.context, replacement),
};
} else {
console.warn('Invalid login response template');
}
}
super(entitySetting, 'idp');
}
/**
* @desc Generates the login response for developers to design their own method
* @param sp object of service provider
* @param requestInfo corresponding request, used to obtain the id
* @param binding protocol binding
* @param user current logged user (e.g. req.user)
* @param customTagReplacement used when developers have their own login response template
* @param encryptThenSign whether or not to encrypt then sign first (if signing)
* @param relayState the relayState from corresponding request
*/
public async createLoginResponse(
sp: ServiceProvider,
requestInfo: { [key: string]: any },
binding: string,
user: { [key: string]: any },
customTagReplacement?: (template: string) => BindingContext,
encryptThenSign?: boolean,
relayState?: string,
) {
const protocol = namespace.binding[binding];
// can support post, redirect and post simple sign bindings for login response
let context: any = null;
switch (protocol) {
case namespace.binding.post:
context = await postBinding.base64LoginResponse(requestInfo, {
idp: this,
sp,
}, user, customTagReplacement, encryptThenSign);
break;
case namespace.binding.simpleSign:
context = await simpleSignBinding.base64LoginResponse( requestInfo, {
idp: this, sp,
}, user, relayState, customTagReplacement);
break;
case namespace.binding.redirect:
return redirectBinding.loginResponseRedirectURL(requestInfo, {
idp: this,
sp,
}, user, relayState, customTagReplacement);
default:
throw new Error('ERR_CREATE_RESPONSE_UNDEFINED_BINDING');
}
return {
...context,
relayState,
entityEndpoint: (sp.entityMeta as ServiceProviderMetadata).getAssertionConsumerService(binding) as string,
type: 'SAMLResponse'
};
}
/**
* Validation of the parsed URL parameters
* @param sp ServiceProvider instance
* @param binding Protocol binding
* @param req RequesmessageSigningOrderst
*/
parseLoginRequest(sp: ServiceProvider, binding: string, req: ESamlHttpRequest) {
const self = this;
return flow({
from: sp,
self: self,
checkSignature: self.entityMeta.isWantAuthnRequestsSigned(),
parserType: 'SAMLRequest',
type: 'login',
binding: binding,
request: req
});
}
}