func (xmlNode *XmlNode) DuplicateTo(doc Document, level int) (dup Node) { if xmlNode.valid { dupPtr := C.xmlDocCopyNode(xmlNode.Ptr, (*C.xmlDoc)(doc.DocPtr()), C.int(level)) if dupPtr != nil { dup = NewNode(unsafe.Pointer(dupPtr), xmlNode.Document) } } return }
func (xp *Xp) CopyNode(node *C.xmlNode, extended int) (copy *C.xmlNode) { copy = C.xmlDocCopyNode(node, xp.doc, C.int(extended)) return }
// NewResponse - create a new response using the supplied metadata and resp. authnrequest and response for filling out the fields // The response is primarily for the attributes, but other fields is eg. the AuthnContextClassRef is also drawn from it func NewResponse(params IdAndTiming, idpmd, spmd, authnrequest, sourceResponse *Xp) (response *Xp) { template := []byte(`<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="" Version="2.0" IssueInstant="" InResponseTo="" Destination="" > <saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"></saml:Issuer> <samlp:Status> <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" /> </samlp:Status> <saml:Assertion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="" Version="2.0" IssueInstant="" > <saml:Issuer></saml:Issuer> <saml:Subject> <saml:NameID SPNameQualifier="" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient" ></saml:NameID> <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"> <saml:SubjectConfirmationData NotOnOrAfter="" Recipient="" InResponseTo="" /> </saml:SubjectConfirmation> </saml:Subject> <saml:Conditions NotBefore="" NotOnOrAfter="" > <saml:AudienceRestriction> <saml:Audience></saml:Audience> </saml:AudienceRestriction> </saml:Conditions> <saml:AuthnStatement AuthnInstant="" SessionNotOnOrAfter="" SessionIndex="" > <saml:AuthnContext> <saml:AuthnContextClassRef></saml:AuthnContextClassRef> </saml:AuthnContext> </saml:AuthnStatement> <saml:AttributeStatement xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema"> </saml:AttributeStatement> </saml:Assertion> </samlp:Response>`) response = NewXp(template) issueInstant := params.Now.Format(xsDateTime) assertionIssueInstant := params.Now.Format(xsDateTime) assertionNotOnOrAfter := params.Now.Add(params.Slack).Format(xsDateTime) sessionNotOnOrAfter := params.Now.Add(params.Sessionduration).Format(xsDateTime) msgid := params.Id if msgid == "" { msgid = Id() } assertionID := params.Assertionid if assertionID == "" { assertionID = Id() } spEntityID := spmd.Query1(nil, `/md:EntityDescriptor/@entityID`) idpEntityID := idpmd.Query1(nil, `/md:EntityDescriptor/@entityID`) acs := authnrequest.Query1(nil, "@AssertionConsumerServiceURL") response.QueryDashP(nil, "./@ID", msgid, nil) response.QueryDashP(nil, "./@IssueInstant", issueInstant, nil) response.QueryDashP(nil, "./@InResponseTo", authnrequest.Query1(nil, "@ID"), nil) response.QueryDashP(nil, "./@Destination", acs, nil) response.QueryDashP(nil, "./saml:Issuer", idpEntityID, nil) assertion := response.Query(nil, "saml:Assertion")[0] response.QueryDashP(assertion, "@ID", assertionID, nil) response.QueryDashP(assertion, "@IssueInstant", assertionIssueInstant, nil) response.QueryDashP(assertion, "saml:Issuer", idpEntityID, nil) nameid := response.Query(assertion, "saml:Subject/saml:NameID")[0] response.QueryDashP(nameid, "@SPNameQualifier", spEntityID, nil) response.QueryDashP(nameid, "@Format", sourceResponse.Query1(nil, "//saml:NameID/@Format"), nil) response.QueryDashP(nameid, ".", sourceResponse.Query1(nil, "//saml:NameID"), nil) subjectconfirmationdata := response.Query(assertion, "saml:Subject/saml:SubjectConfirmation/saml:SubjectConfirmationData")[0] response.QueryDashP(subjectconfirmationdata, "@NotOnOrAfter", assertionNotOnOrAfter, nil) response.QueryDashP(subjectconfirmationdata, "@Recipient", acs, nil) response.QueryDashP(subjectconfirmationdata, "@InResponseTo", authnrequest.Query1(nil, "@ID"), nil) conditions := response.Query(assertion, "saml:Conditions")[0] response.QueryDashP(conditions, "@NotBefore", assertionIssueInstant, nil) response.QueryDashP(conditions, "@NotOnOrAfter", assertionNotOnOrAfter, nil) response.QueryDashP(conditions, "saml:AudienceRestriction/saml:Audience", spEntityID, nil) authstatement := response.Query(assertion, "saml:AuthnStatement")[0] response.QueryDashP(authstatement, "@AuthnInstant", assertionIssueInstant, nil) response.QueryDashP(authstatement, "@SessionNotOnOrAfter", sessionNotOnOrAfter, nil) response.QueryDashP(authstatement, "@SessionIndex", "missing", nil) response.QueryDashP(authstatement, "saml:AuthnContext/saml:AuthnContextClassRef", sourceResponse.Query1(nil, "//saml:AuthnContextClassRef"), nil) sourceResponse = NewXp([]byte(sourceResponse.Pp())) sourceAttributes := sourceResponse.Query(nil, `//saml:AttributeStatement/saml:Attribute`) destinationAttributes := response.Query(nil, `//saml:AttributeStatement`)[0] attrcache := map[string]*C.xmlNode{} for _, attr := range sourceAttributes { attrcache[attr.GetAttr("Name")] = attr attrcache[attr.GetAttr("FriendlyName")] = attr } //requestedAttributes := spmd.Query(nil, `./md:SPSSODescriptor/md:AttributeConsumingService[1]/md:RequestedAttribute[@isRequired=true()]`) requestedAttributes := spmd.Query(nil, `./md:SPSSODescriptor/md:AttributeConsumingService[1]/md:RequestedAttribute`) for _, requestedAttribute := range requestedAttributes { // for _, requestedAttribute := range sourceResponse.Query(nil, `//saml:Attribute`) { name := requestedAttribute.GetAttr("Name") friendlyname := requestedAttribute.GetAttr("FriendlyName") //nameFormat := requestedAttribute.GetAttr("NameFormat") //log.Println("requestedattribute:", name, nameFormat) // look for a requested attribute with the requested nameformat // TO-DO - xpath escape name and nameFormat // TO-Do - value filtering //attributes := sourceResponse.Query(sourceAttributes[0], `saml:Attribute[@Name="`+name+`" or @Name="`+friendlyname+`" or @FriendlyName="`+friendlyname+`"]`) //log.Println("src attrs", len(attributes), `saml:Attribute[@Name="`+name+`" or @Name="`+friendlyname+`" or @FriendlyName="`+friendlyname+`"]`) //attributes := sourceResponse.Query(sourceAttributes, `saml:Attribute[@Name="`+name+`"]`) attribute := attrcache[name] if attribute == nil { attribute = attrcache[friendlyname] if attribute == nil { continue } } // for _, attribute := range sourceAttributes { newAttribute := C.xmlAddChild(destinationAttributes, C.xmlDocCopyNode(attribute, response.doc, 2)) allowedValues := spmd.Query(requestedAttribute, `saml:AttributeValue`) allowedValuesMap := make(map[string]bool) for _, value := range allowedValues { allowedValuesMap[spmd.NodeGetContent(value)] = true } for _, valueNode := range sourceResponse.Query(attribute, `saml:AttributeValue`) { value := sourceResponse.NodeGetContent(valueNode) if len(allowedValues) == 0 || allowedValuesMap[value] { C.xmlAddChild(newAttribute, C.xmlDocCopyNode(valueNode, response.doc, 1)) } } // } } return }
// NewXpFromNode creates a new *Xp from a node (subtree) from another *Xp func NewXpFromNode(node *C.xmlNode) *Xp { xp := NewXp(nil) C.xmlAddChild((*C.xmlNode)(unsafe.Pointer(xp.doc)), C.xmlDocCopyNode(node, xp.doc, 1)) return xp }