feat(cloudid): add azure saml

This commit is contained in:
Qu Xuan
2021-01-18 14:58:19 +08:00
parent be959d9459
commit 627503cdc1
37 changed files with 916 additions and 32 deletions
+25
View File
@@ -194,6 +194,27 @@ func prepareServer() error {
Values: []string{v.value},
})
}
case "urn:federation:MicrosoftOnline":
data.NameId = sp.Username
data.NameIdFormat = samlutils.NAME_ID_FORMAT_PERSISTENT
data.AudienceRestriction = sp.GetEntityId()
for _, v := range []struct {
name string
friendlyName string
value string
}{
{
name: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
value: data.NameId,
},
} {
data.Attributes = append(data.Attributes, samlutils.SSAMLResponseAttribute{
Name: v.name,
FriendlyName: v.friendlyName,
Values: []string{v.value},
})
}
return data, nil
case "google.com/a/yunion-hk.com":
data.NameId = "qiujian"
data.NameIdFormat = samlutils.NAME_ID_FORMAT_TRANSIENT
@@ -406,6 +427,10 @@ func prepareServer() error {
name: "Google cloud SSO",
url: "https://www.google.com/a/yunion-hk.com/ServiceLogin?continue=https://console.cloud.google.com",
},
{
name: "Azure cloud SSO",
url: "https://login.microsoftonline.com/redeem?rd=https%3a%2f%2finvitations.microsoft.com%2fredeem%2f%3ftenant%3d17493ddf-fa90-4f95-8576-5df011c126e5%26user%3d3bc1c055-aa14-4795-aef0-5970b00d03c7%26ticket%3d0GDu%252bZ7nLbg01rYL5u%252b401%252bOLyZjxPewSBJIAZZ7E0U%253d%26ver%3d2.0",
},
} {
htmlBuf.WriteString(fmt.Sprintf(`<li><a href="%s">%s (SP-Initiated)</a></li>`, v.url, v.name))
}
+8 -4
View File
@@ -77,6 +77,7 @@ func (idp *SSAMLIdpInstance) AddHandlers(app *appsrv.Application, prefix string,
if middleware != nil {
handler = middleware(handler)
}
app.AddHandler("POST", idp.redirectLoginPath, handler)
app.AddHandler("GET", idp.redirectLoginPath, handler)
handler = idp.redirectLogoutHandler
if middleware != nil {
@@ -224,16 +225,17 @@ func (idp *SSAMLIdpInstance) processLoginRequest(ctx context.Context, idpId stri
return "", errors.Wrapf(httperrors.ErrInputParameter, "Destination not match: get %s want %s", authReq.Destination, idp.getRedirectLoginUrl(idpId))
}
if authReq.AssertionConsumerServiceURL != sp.GetPostAssertionConsumerServiceUrl() {
if len(authReq.AssertionConsumerServiceURL) > 0 && authReq.AssertionConsumerServiceURL != sp.GetPostAssertionConsumerServiceUrl() {
return "", errors.Wrapf(httperrors.ErrInputParameter, "AssertionConsumerServiceURL not match: get %s want %s", authReq.AssertionConsumerServiceURL, sp.GetPostAssertionConsumerServiceUrl())
}
sp.Username = input.Username
resp, err := idp.getLoginResponse(ctx, authReq, idpId, sp)
if err != nil {
return "", errors.Wrap(err, "getLoginResponse")
}
form, err := idp.samlResponse2Form(authReq.AssertionConsumerServiceURL, resp, input.RelayState)
form, err := idp.samlResponse2Form(sp.GetPostAssertionConsumerServiceUrl(), resp, input.RelayState)
if err != nil {
return "", errors.Wrap(err, "samlResponse2Form")
}
@@ -246,7 +248,6 @@ func (idp *SSAMLIdpInstance) samlResponse2Form(url string, resp *samlutils.Respo
if err != nil {
return "", errors.Wrap(err, "xml.Marshal")
}
signed, err := idp.saml.SignXML(string(respXml))
if err != nil {
return "", errors.Wrap(err, "saml.SignXML")
@@ -287,7 +288,7 @@ func (idp *SSAMLIdpInstance) getLoginResponse(ctx context.Context, req samlutils
IssuerEntityId: idp.saml.GetEntityId(),
RequestID: req.ID,
RequestEntityId: req.Issuer.Issuer,
AssertionConsumerServiceURL: req.AssertionConsumerServiceURL,
AssertionConsumerServiceURL: sp.GetPostAssertionConsumerServiceUrl(),
SSAMLSpInitiatedLoginData: data,
}
resp := samlutils.NewResponse(input)
@@ -303,6 +304,9 @@ func (idp *SSAMLIdpInstance) processIdpInitiatedLogin(ctx context.Context, input
if err != nil {
return "", errors.Wrap(err, "idp.onIdpInitiatedLogin")
}
if len(data.Form) > 0 {
return data.Form, nil
}
respInput := samlutils.SSAMLResponseInput{
IssuerCertString: idp.saml.GetCertString(),
IssuerEntityId: idp.saml.GetEntityId(),
+2
View File
@@ -23,6 +23,8 @@ import (
type SSAMLServiceProvider struct {
desc samlutils.EntityDescriptor
Username string
}
func (sp *SSAMLServiceProvider) GetEntityId() string {
+4 -1
View File
@@ -36,6 +36,8 @@ type SSAMLSpInitiatedLoginData struct {
AudienceRestriction string
Attributes []SSAMLResponseAttribute
Form string
}
type SSAMLIdpInitiatedLoginData struct {
@@ -59,7 +61,7 @@ type SSAMLResponseInput struct {
func NewResponse(input SSAMLResponseInput) Response {
// since := timeutils.IsoTime(time.Now().UTC().Add(-time.Minute * 60 * 24))
until := timeutils.IsoTime(time.Now().UTC().Add(time.Minute * 60 * 24))
until := timeutils.IsoTime(time.Now().UTC().Add(time.Minute * 5))
respId := GenerateSAMLId()
assertId := GenerateSAMLId()
@@ -251,6 +253,7 @@ func NewResponse(input SSAMLResponseInput) Response {
Space: XMLNS_ASSERT,
Local: "Conditions",
},
NotBefore: &now,
NotOnOrAfter: until,
AudienceRestrictions: []AudienceRestriction{},
},
+2
View File
@@ -250,6 +250,8 @@ type SIdpRedirectLoginInput struct {
RelayState string `json:"RelayState,ignoreempty"`
SigAlg string `json:"SigAlg,ignoreempty"`
Signature string `json:"Signature,ignoreempty"`
Username string `json:"username,ignoreempty"`
}
type SIdpInitiatedLoginInput struct {
+10 -5
View File
@@ -25,6 +25,7 @@ import (
"github.com/ma314smith/signedxml"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/utils"
)
@@ -72,11 +73,15 @@ func SAMLDecode(input string) ([]byte, error) {
if err != nil {
return nil, errors.Wrap(err, "base64.StdEncoding.DecodeString")
}
plainText, err := decompress(reqBytes)
if err != nil {
return nil, errors.Wrap(err, "decompress")
}
return plainText, nil
return func() []byte {
// Azure no need to decompress
plainText, err := decompress(reqBytes)
if err != nil {
log.Warningf("decompress %s error: %v", string(reqBytes), err)
return reqBytes
}
return plainText
}(), nil
}
func SAMLEncode(input []byte) (string, error) {