I have created an IDP using the code contained within https://github.com/ITfoxtec/ITfoxtec.Identity.Saml2/blob/master/test/TestIdPCore/Controllers/AuthController.cs
This is throwing an error when I attempt to bind the authNResponse using the following code:
var responsebinding = new Saml2PostBinding();
responsebinding.Bind(saml2AuthnResponse).XmlDocument.OuterXml;
This is the same code as within the PostContent method, but I've opted to use this code direct as I just needed the SamlResponse.
The error is:
Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenWriteException: 'IDX13129: The SAML2:AttributeStatement must contain at least one SAML2:Attribute.'
With the following abridged stack trace:
at Microsoft.IdentityModel.Tokens.Saml2.Saml2Serializer.WriteAttributeStatement(XmlWriter writer, Saml2AttributeStatement statement)
at Microsoft.IdentityModel.Tokens.Saml2.Saml2Serializer.WriteStatement(XmlWriter writer, Saml2Statement statement)
at Microsoft.IdentityModel.Tokens.Saml2.Saml2Serializer.WriteAssertion(XmlWriter writer, Saml2Assertion assertion)
at Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler.WriteToken(XmlWriter writer, SecurityToken securityToken)
at ITfoxtec.Identity.Saml2.Tokens.Saml2ResponseSecurityTokenHandler.WriteToken(SecurityToken token)
at ITfoxtec.Identity.Saml2.Saml2AuthnResponse.ToXml()
I have used your example code almost exactly, so is there an issue within it, or am I missing something?
Many thanks
Here's a deeper analysis and two possible solutions/workarounds.
The situation: creating a ITfoxtec.Identity.Saml2.Saml2AuthnResponse for a ClaimsIdentity that has only one claim: the nameidentifier.
The relevant code snippet (not complete, just the part that is relevant, but the ITFoxttec samples have the full code)
var response = new Saml2AuthnResponse(config);
response.ClaimsIdentity = new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, "someone#somewhere.com") });
response.NameId = new Saml2NameIdentifier(....etc...);
var token = response.CreateSecurityToken(appliesToAddress);
//so far all is well, but the problem has been sneakily introduced!
//which is why the next line will give the error: Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenWriteException: 'IDX13129: The SAML2:AttributeStatement must contain at least one SAML2:Attribute
return binding.Bind(response).ToActionResult();
Explanation:
ITfoxtec code: The nameidentifier claim is is removed from the claims when the token is created. This makes senses as it is supposed to be in the NameId property. This remaining claims are set as Subject in the SecurityTokenDescriptor that is fed to the Saml2SecurityTokenHandler which is Microsoft code.
var tokenDescriptor = new SecurityTokenDescriptor();
tokenDescriptor.Subject = new ClaimsIdentity(claims.Where(c => c.Type != ClaimTypes.NameIdentifier));
The claims in this tokendescriptor then end up as Attributes in the AttributeStatement in the generated Saml2SecurityToken (via a Saml2SecurityTokenHandler.CreateToken(tokendescriptor) call).
Unfortunately, if the nameidentifier was the only claim you had, then you end up with an AttributeStatement that has no Attributes. And subsequently run into the problem when the binding.Bind(response) deep down the bowels does its XML thing..
Unless you are supposed to always have an AttributeStatement it looks to me like a bug / edge case in the Microsoft.IdentityModel.Tokens.Saml library.
There are two solutions to solve it:
Prevent ending up with no claims: Simply add another claim to the identity, doesn't have to be email, can be anything:
response.ClaimsIdentity.AddClaim(new Claim("x", "y"))
After the CreateSecurityToken call but before the call to Bind, check if the AttributeStatement is empty and if so remove it. A quick and dirty example for that:
var x = (Saml2AttributeStatement)token.Assertion.Statements.FirstOrDefault(a => a.GetType() == typeof(Saml2AttributeStatement));
if (x?.Attributes.Count == 0)
{
token.Assertion.Statements.Remove(x);
}
Personally, I prefer option 1, as it is generally safer to use and less code. Plus I'm sure there can always be 'something' to further attribute the identity with...
Maybe you are missing the part of adding claims to the token and creating the token?
saml2AuthnResponse.SessionIndex = sessionIndex;
var claimsIdentity = new ClaimsIdentity(claims);
saml2AuthnResponse.NameId = new Saml2NameIdentifier(claimsIdentity.Claims.Where(c => c.Type == ClaimTypes.NameIdentifier).Select(c => c.Value).Single(), NameIdentifierFormats.Persistent);
saml2AuthnResponse.ClaimsIdentity = claimsIdentity;
var token = saml2AuthnResponse.CreateSecurityToken(relyingParty.Issuer, subjectConfirmationLifetime: 5, issuedTokenLifetime: 60);
https://github.com/ITfoxtec/ITfoxtec.Identity.Saml2/blob/master/test/TestIdPCore/Controllers/AuthController.cs#L110
I have found that you need both the ClaimTypes.NameIdentifier and ClaimTypes.Email claims in order for the token to be generated successfully.
I can send a parameter from as3 to asp. And I can get a value from db. But unfortunatelly I cant combine both of them. Is it possible to send a ID parameter from as3 to asp where I want to make a sql query on a db. Then query result will return back to the as3. Users can login with their id number. And they can see their own datas on the as3 application. My sample codes are given:
I can send values with these codes:
var getParams:URLRequest = new URLRequest("http://www***********/data.asp");
getParams.method = URLRequestMethod.POST;
var paras:URLVariables = new URLVariables();
paras.parameter1 = ""+userID;
getParams.data = paras;
var loadPars:URLLoader = new URLLoader(getParams);
loadPars.addEventListener(Event.COMPLETE, loadCompleted);
loadPars.dataFormat = URLLoaderDataFormat.VARIABLES;
loadPars.load(getParams);
function loadCompleted(event:Event):void
{
trace("sent")
}
I can get values from db with these codes:
var urlLoader:URLLoader =new URLLoader();
urlLoader.load(new URLRequest("http://www***********/data.asp"));
urlLoader.dataFormat = URLLoaderDataFormat.VARIABLES;
urlLoader.addEventListener(Event.COMPLETE, onXMLLoad);
function onXMLLoad(event:Event):void
{
var loader:URLLoader = URLLoader(event.target);
var scrptVars:URLVariables = new URLVariables(loader.data +"");
returnParameter= scrptVars.LINK0;
high.HighScore.text = returnParameter + "";
}
What is the logic of combining them?
Sory for my English level :)
To combine the second one into the first, you just need to read the URLLoader's data property (which is the response from the server) on the loadCompleted method (same as you're doing in the onXMLLoad method):
function loadCompleted(event:Event):void
{
trace("sent and received", loadPars.data);
high.HighScore.text = loadPars.data.LINK0;
}
The COMPLETE event for a URLLoader fires once the request has received a response. If your server adds data to that response, it can be found in the data property of the URLLoader.
So to summarize, sending and receiving can be done all in one operation with one URLLoader. The data you send to the server, is found in the URLRequest object passed to the URLLoader, the data that comes back from that request, is found in the data property of the URLLoader object (but only after the COMPLETE event fires).
Hy, I have some problems with the Go endpoints and Dart client library.
I use the Go library https://github.com/crhym3/go-endpoints and the dart generator https://github.com/dart-lang/discovery_api_dart_client_generator
The easy examples works fine. But they show never how to use time.Time.
In my project, I have a struct with a field:
Created time.Time `json:"created"`
The output in the explorer looks like this:
"created": "2014-12-08T20:42:54.299127593Z",
When i use it in the dart client library, I get the error
FormatException: Invalid date format 2014-12-08T20:53:56.346129718Z
Should I really format every time fields in the go app (Format Timestamp in outgoing JSON in Golang?)?
My research come to that the dart accept something:
t.Format(time.RFC3339) >> 2014-12-08T20:53:56Z
Second problem, if comment out the Created field or leave it blank. I get a other error:
The null object does not have a method 'map'.
NoSuchMethodError: method not found: 'map' Receiver: null Arguments:
[Closure: (dynamic) => dynamic]
But I can't figure it out which object is null. I'm not sure if I'm using the Dart client correct
import 'package:http/browser_client.dart' as http;
...
var nameValue = querySelector('#name').value;
var json = {'name':nameValue};
LaylistApi api = new LaylistApi(new http.BrowserClient());
api.create(new NewLayListReq.fromJson(json)).then((LayList l) {
print(l);
}).catchError((e) {
querySelector('#err-message').innerHtml=e.toString();
});
Does anyone know of a larger project on github with Go endpoint and Dart?
Thanks for any advice
UPDATE[2014-12-11]:
I fixed the
NoSuchMethodError
with the correct discovery url https://constant-wonder-789.appspot.com/_ah/api/discovery/v1/apis/greeting/v1/rest
The problem with the time FormatExcetion still open, but I'm one step further. If i create a new item, it doesn' work. But if I load the items from the datastore and send it back, this works.
I guess this can be fixed with implementing Marshaler interface, thanks Alex. I will update my source soon.
See my example:
http://constant-wonder-789.appspot.com/
The full source code:
https://github.com/cloosli/greeting-example
I'm trying to use cloud code to create a new 'credit' every time a new User is created, the credit is for that user, as in it is a related object. For some reason I can't get writing to the 'Logs' tab to work using lines like console.log(tell me what is going on!); so I'm stumped, and with no way of knowing where I've gone wrong.
Parse.Cloud.afterSave("User", function(request) {
var Credit = Parse.Object.extend("credit");
var credit = new Credit();
credit.set("parent", request.object);
credit.set("expiry", null);
credit.set("type", "Opening");
credit.save();
});
You need to change it to this:
Parse.Cloud.afterSave(Parse.User, function(request)
Parse.User rather than "User".
For whatever reason this doesn't seem to be in the docs here: https://www.parse.com/docs/cloud_code_guide#functions-aftersave
I'm developing a web application using GeoExt, OpenLayers and having my own GeoServer to serve various maps. Still, I want to let the user add other WMS's if needed, to be able to play around with all desired layers.
Thus, my problem with the GetFeatureInfo request. Right now I have a toolbar button attached to geoext's map panel,
new GeoExt.Action({
iconCls: "feature",
map: map,
toggleGroup: "tools",
tooltip: "Feature",
control: featureControl
})
its control attribute being
var featureControl = new OpenLayers.Control.WMSGetFeatureInfo({
queryVisible: true,
drillDown: true,
infoFormat:"application/vnd.ogc.gml"
});
I've also defined an event listener to do what I really want once I receive the responses, but that is not relevant here. My problem is the following:
Considering the user clicks on a point where there are 2+ visible layers and at least one of them is from a different source, OpenLayers will have to do one AJAX request per different source and, from OpenLayers own documentation,
Triggered when a GetFeatureInfo response is received. The event
object has a text property with the body of the response (String), a
features property with an array of the parsed features, an xy property
with the position of the mouse click or hover event that triggered the
request, and a request property with the request itself. If drillDown
is set to true and multiple requests were issued to collect feature
info from all layers, text and request will only contain the response
body and request object of the last request.
so, yeah, it will obviously wont work like that right away. Having a look at the debugger I can clearly see that, giving two layers from different sources, it actually DOES the request, it's just that it doesn't wait for the first's response and jumps for the next one (obviously, being asynchronous). I've thought about doing the requests one-by-one, meaning doing the first one as stated above and once it's finished and the response saved, go for the next one. But I'm still getting used to the data structure GeoExt uses.
Is there any API (be it GeoExt or OpenLayers) option/method I'm missing? Any nice workarounds?
Thanks for reading :-)
PS: I'm sorry if I've not been clear enough, english is not my mother tongue. Let me know if something stated above was not clear enough :)
i Hope this help to someone else, I realized that: you're rigth this control make the request in asynchronous mode, but this is ok, no problem with that, the real problem is when the control handle the request and trigger the event "getfeatureinfo" so, i modified 2 methods for this control and it works!, so to do this i declare the control first, and then in the savage mode i modified the methods here is de code:
getInfo = new OpenLayers.Control.WMSGetFeatureInfo({ drillDown:true , queryVisible: true , maxFeatures:100 });
//then i declare a variable that help me to handle more than 1 request.....
getInfo.responses = [];
getInfo.handleResponse=function(xy, request) { var doc = request.responseXML;
if(!doc || !doc.documentElement) { doc = request.responseText; }
var features = this.format.read(doc);
if (this.drillDown === false) {
this.triggerGetFeatureInfo(request, xy, features);
} else {
this._requestCount++;
this._features = (this._features || []).concat(features);
if( this._numRequests > 1){
//if the num of RQ, (I mean more than 1 resource ), i put the Request in array, this is for maybe in a future i could be need other properties or methods from RQ, i dont know.
this.responses.push(request);}
else{
this.responses = request;}
if (this._requestCount === this._numRequests) {
//here i change the code....
//this.triggerGetFeatureInfo(request, xy, this._features.concat());
this.triggerGetFeatureInfo(this.responses, xy, this._features.concat());
delete this._features;
delete this._requestCount;
delete this._numRequests;
// I Adding this when the all info is done 4 reboot
this.responses=[];
}
}
}
getInfo.triggerGetFeatureInfo= function( request , xy , features) {
//finally i added this code for get all request.responseText's
if( isArray( request ) ){
text_rq = '';
for(i in request ){
text_rq += request[i].responseText;
}
}
else{
text_rq = request.responseText;
}
this.events.triggerEvent("getfeatureinfo", {
//text: request.responseText,
text : text_rq,
features: features,
request: request,
xy: xy
});
// Reset the cursor.
OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait");}
Thanks, you bring me a way for discover my problem and here is the way i solved, i hope this can help to somebody else.
saheka's answer was almost perfect! Congratulations and thank you, I had the same problem, and with it I finally managed to solve it.
What I would change in your code:
isArray() does not work, change it like this: if(request instanceof Array) {...} at the first line of getInfo.triggerGetFeatureInfo()
to show the results in a popup this is the way:
My code:
getInfo.addPopup = function(map, text, xy) {
if(map.popups.length > 0) {
map.removePopup(map.popups[0]);
}
var popup = new OpenLayers.Popup.FramedCloud(
"anything",
map.getLonLatFromPixel(xy),
null,
text,
null,
true
);
map.addPopup(popup);
}
and in the getInfo.triggerGetFeatureInfo() function, after the last line, append:
this.addPopup(map, text_rq, xy);
A GetFeatureInfo request is send as a JavaScript Ajax call to the external server. So, the requests are likely blocked for security reasons. You'll have to send the requests to the external servers by a proxy on your own domain.
Then, configure this proxy in openlayers by setting OpenLayers.ProxyHost to the proper path. For example:
OpenLayers.ProxyHost = "/proxy_script";
See http://trac.osgeo.org/openlayers/wiki/FrequentlyAskedQuestions#ProxyHost for more background information.