I had a Dto with the following route defined
[Route("/route/{Id}/{Status}")]
public class JustIdAndStatus : IReturn {
public long Id { get; set; }
public long Status { get; set; }
}
Then I invoke the service with something like
localhost:9040/route/1/100
or
localhost:9040/route/0/100
and everything goes well.
but when I use one of the service clients I got an exception if any of the values is 0 (default value for the long).
var client = new JsonServiceClient("http://127.0.0.1:9040");
var request = new JustIdAndStatus() {
Id = 0,
Status = 1
};
var response = client.Get(request);
the exception I get is
System.InvalidOperationException : None of the given rest routes matches 'JustIdAndStatus' request:
/route/{Id}/{Status}: Could not match following variables: Id
at ServiceStack.ServiceClient.Web.UrlExtensions.ToUrl(IReturn request, String httpMethod, String formatFallbackToPredefinedRoute) in UrlExtensions.cs: line 65
at ServiceStack.Common.Tests.UrlExtensionTests.Can_create_url_with_JustIdAndStatus(Int64 id, Int64 status) in UrlExtensionTests.cs: line 91
I tracked down the issue to this commit on the service stack repository
https://github.com/ServiceStack/ServiceStack/commit/b34a5906a265da19d916ea47ee80783bd866abcb
I notice that when I updated from ServiceStack 3.9.38 to 3.9.40 from nuget.
I want to know if this behavior is right, so I'm using the routes in a wrong way or maybe this is an issue and can be submited to the issue tracker on github.
also I make a test using the basic ones I found on ServiceStack.Commons source code
https://gist.github.com/anonymous/5216727
Thanks in advance!
I agree with you that what you describe should be expected behaviour where the property is specifically referenced in the route.
The commit you refer to was intended to suppress the use of default parameter values on routes where members were relegated to the query string. However, it also appears to have caused the issue you describe with explicit routes.
I am putting together a pull request to address the issue and add some tests to reflect the expected behaviour.
Related
I've been working on how to save OpenIdConnecConfiguration locally in the odd case that the AuthServer is not reachable but the frontend client (e.g. Phone) still has a valid refresh token which still needs to be validated again when signing in. It is also needed to be saved locally to a file in the case that the backend (e.g. WCF) has restarted due to a update or the frequent restarts it has (once a day)
What I've done so far, I've saved the JSON object of the ".well-known/openid-configuration" to a file/variable and now I want to create the OpenIdConnectConfiguration object.
OpenIdConnectConfiguration.Create(json) does a lot of the work but the signingKeys do not get created. I think maybe it's because the authorization endpoint needs to be created in some other manner maybe?
Or maybe I'm doing this the wrong way and there is another solution to this issue. I'm working in C#.
Edit: I know there are some caveats to what I'm doing. I need to check once in awhile to see if the public key has been changed, but security wise it should be fine to save the configuration because it's already public. I only need the public key to validate/sign the jwt I get from the user and nothing more.
Figured out a solution after looking through OpenIdConnectConfiguration.cs on the official github.
When fetching the OpenIdConnectConfiguration the first time, use Write() to get a JSON string and use it to save it to file.
Afterwards when loading the file, use Create() to create the OpenIdConnectConfiguration again from the JSON string (This had the issue of not saving the signingKeys as said in the question, but alas! there is a fix)
Lastly to fix the issue with the signingKeys not being created, (this is what I found out from the github class) all we need to do is loop through the JsonWebKeySet and create them as is done in the class. We already have all the information needed from the initial load and therefore only need to create them again.
I'll leave the code example below of what I did. I still need to handle checking if he key has been changed/expired which is the next step I'll be tackling.
interface IValidationPersistence
{
void SaveOpenIdConnectConfiguration(OpenIdConnectConfiguration openIdConfig);
OpenIdConnectConfiguration LoadOpenIdConnectionConfiguration();
}
class ValidationPersistence : IValidationPersistence
{
private readonly string _windowsTempPath = Path.GetTempPath();
private readonly string _fileName = "TestFileName";
private readonly string _fullFilePath;
public ValidationPersistence()
{
_fullFilePath = _windowsTempPath + _fileName;
}
public OpenIdConnectConfiguration LoadOpenIdConnectionConfiguration()
{
FileService fileService = new FileService();
OpenIdConnectConfiguration openIdConfig = OpenIdConnectConfiguration.Create(fileService.LoadFromJSONFile<string>(_fullFilePath));
foreach (SecurityKey key in openIdConfig.JsonWebKeySet.GetSigningKeys())
{
openIdConfig.SigningKeys.Add(key);
}
return openIdConfig;
}
public void SaveOpenIdConnectConfiguration(OpenIdConnectConfiguration openIdConfig)
{
FileService fileService = new FileService();
fileService.WriteToJSONFile(OpenIdConnectConfiguration.Write(openIdConfig), _fullFilePath);
}
}
I have a business requirement to only send permissioned properties in our response payload. For instance, our response DTO may have several properties, and one of them is SSN. If the user doesn't have permissions to view the SSN then I would never want it to be in the Json response. The second requirement is that we send null values if the client has permissions to view or change the property. Because of the second requirement setting the properties that the user cannot view to null will not work. I have to still return null values.
I have a solution that will work. I create an expandoObject by reflecting through my DTO and add only the properties that I need. This is working in my tests.
I have looked at implementing ITextSerializer. I could use that and wrap my response DTO in another object that would have a list of properties to skip. Then I could roll my own SerializeToString() and SerializeToStream(). I don't really see any other ways at this point. I can't use the JsConfig and make a SerializeFn because the properties to skip would change with each request.
So I think that implementing ITextSerializer is a good option. Are there any good examples of this getting implemented? I would really like to use all the hard work that was already done in the serializer and take advantage of the great performance. I think that in an ideal world I would just need to add a check in the WriteType.WriteProperties() to look and the property is one to write, but that is internal and really, most of them are so I can't really take advantage of them.
If someone has some insight please let me know! Maybe I am making the implementation of ITextSerialzer a lot harder that it really is?
Thanks!
Pull request #359 added the property "ExcludePropertyReference" to the JsConfig and the JsConfigScope. You can now exclude references in scope like I needed to.
I would be hesitant to write my own Serializer. I would try to find solutions that you can plug in into the existing ServiceStack code. That way you will have to worry less about updating dlls and breaking changes.
One potential solution would be decorating your properties with a Custom Attributes that you could reflect upon and obscure the property values. This could be done in the Service before Serialization even happens. This would still include values that they user does not have permission to see but I would argue that if you null those properties out they won't even be serialized by JSON anyways. If you keep all the properties the same they you will keep the benefits of strong typed DTOs.
Here is some hacky code I quickly came up with to demonstrate this. I would move this into a plugin and make the reflection faster with some sort of property caching but I think you will get the idea.
Hit the url twice using the following routes to see it in action.
/test?role
/test?role=Admin (hack to pretend to be an authenticated request)
[System.AttributeUsage(System.AttributeTargets.Property)]
public class SecureProperty : System.Attribute
{
public string Role {get;set;}
public SecureProperty(string role)
{
Role = role;
}
}
[Route("/test")]
public class Test : IReturn
{
public string Name { get; set; }
[SecureProperty("Admin")]
public string SSN { get; set; }
public string SSN2 { get; set; }
public string Role {get;set;}
}
public class TestService : Service
{
public object Get(Test request)
{
// hack to demo roles.
var usersCurrentRole = request.Role;
var props = typeof(Test).GetProperties()
.Where(
prop => ((SecureProperty[])prop
.GetCustomAttributes(typeof(SecureProperty), false))
.Any(att => att.Role != usersCurrentRole)
);
var t = new Test() {
Name = "Joe",
SSN = "123-45-6789",
SSN2 = "123-45-6789" };
foreach(var p in props) {
p.SetValue(t, "xxx-xx-xxxx", null);
}
return t;
}
}
Require().StartHost("http://localhost:8080/",
configurationBuilder: host => { });
I create this demo in ScriptCS. Check it out.
public class EngineInfo
{
public int Id{get;set;}
public int? AircraftId { get; set; }
public string SerialNumber { get; set; }
public int Position { get; set; }
public string RegNumber { get; set; }
}
// Here is the code which uses the above model. I have 17,000 documents with this model
ravenSession.Store(new AuthorizationUser
{
Id = "Authorization/Users/1",
Name = "user-1",
Permissions =
{
new OperationPermission
{
Allow = true,
Operation = "EngineInfos/View",
Tags = "Company/100"
}
}
});
1. var query = ravenSession.Query<EngineInfo>();
// When I log query.Count(), I see all the documents count ie., 17000, This is ignoring the authorization I set in the before statement. If I add where clause to the above statement it is working and I could see the correct count. But I want to get all the documents for which the user has authorization to.
2. var query = ravenSession.Query<EngineInfo>().ToList();
Now, I get the correct count considering authorization. But the problem is unless I mention Take(x), it will not return all the results.
I tried with
RavenQueryStatistics queryStats;
query.Statistics(out queryStats);
queryStats.TotalResults
I still could not get the authorizes results. I get all the count.
Could you please help me figuring out in finding TotalCount of the query results without loading all records?
My requirement is to display all engines in an searchable ExtJS paging grid. I need to know the total count of the records to display calculate and display the number of pages(page count is fixed).
This is by design, see http://ravendb.net/docs/intro/safe-by-default.
session.Query<Post>().Count() will give you the count of all the posts on the server, while session.Query<Post>().ToList().Count() will give the count of the posts that was fetched to the client.
By default, RavenDB apply .Take(128) to the query, in order to encourage you to do paging and be safe by default. If you want to get more then that you need to specify how much to take, like .Take(1024), but by default the server will not return more then 1024 items at once. You can configure the server to do so, but this is not recommended. You much better use paging as the user cannot handle that much on info at once anyway.
What you're seeing is that Raven's QueryStatistics ignore the Authorization bundle. This has been reported in the Raven Google Group.
As far as I can tell, there isn't, at the time of this writing, a reliable way to get the total count of authorized documents for a query. It seems to me that the Authorization bundle should include some support for this.
I'll look into it and update this answer as I find out more.
I am implementing a RESTful service which will be consumed by a Dojo framewwork's RestStore, which will permit binding the service to various widgets like an interactive grid or a type-ahead ajax select.
The RestStore wants to send and receive the HTTP Range and Content-Range headers in order to restrict the results of the queries to particular subrange.
What is the best practice pattern in ServiceStack (new API) for reacting to HTTP headers as part of the service? Normally, the service method like Get doesn't have access to the HTTP headers, unless I have missed an alternate API.
The current way that I see is to implement an attribute like
public class RangeSupporter : Attribute, IHasRequestFilter, IHasResponseFilter
which will parse the headers on request, and write the headers on response. The DTO would then be marked for this filter.
The filter would transfer the values, say 'First' and 'Last' in and out of the DTO. However, to know that the DTO even has such attributes, it would have to have some marker interface like
interface IHasRangeSupport {
int First { set; get; }
int Last { set; get; }
int Total { set; get; }
}
so that the filter can transfer the information into and out of the DTO the with code similar to:
var r = request as IHasRangeSupport;
if (r != null) {
/// Access the DTO attributes for the range parameters
}
This seems like a lot of ceremony, and a very awkward implementation.
Is there a better pattern for accessing the HTTP headers when implementing a REST service?
In my particular use case, supporting non-REST endpoints (like SOAP) are not required. Only the HTTP end-point is important.
What is the best practice pattern in ServiceStack (new API) for reacting to HTTP headers as part of the service?
I believe you can get the headers in your service class...
public class FooService : Service
{
public object Get(Foo reqeust)
{
//get header
var h1 = base.RequestContext.GetHeader("headerName");
//get header differently
var h2 = base.Request.Headers.Get("headerName");
}
}
Im using subsonic 2.2
I tried asking this question another way but didnt get the answer i was looking for.
Basically i ususally include validation at page level or in my code behind for my user controls or aspx pages. However i haev seen some small bits of info advising this can be done within partial classes generated from subsonic.
So my question is, where do i put these, are there particular events i add my validation / business logic into such as inserting, or updating. - If so, and validation isnt met, how do i stop the insert or update. And if anyone has a code example of how this looks it would be great to start me off.
Any info greatly appreciated.
First you should create a partial class for you DAL object you want to use.
In my project I have a folder Generated where the generated classes live in and I have another folder Extended.
Let's say you have a Subsonic generated class Product. Create a new file Product.cs in your Extended (or whatever) folder an create a partial class Product and ensure that the namespace matches the subsonic generated classes namespace.
namespace Your.Namespace.DAL
{
public partial class Product
{
}
}
Now you have the ability to extend the product class. The interesting part ist that subsonic offers some methods to override.
namespace Your.Namespace.DAL
{
public partial class Product
{
public override bool Validate()
{
ValidateColumnSettings();
if (string.IsNullOrEmpty(this.ProductName))
this.Errors.Add("ProductName cannot be empty");
return Errors.Count == 0;
}
// another way
protected override void BeforeValidate()
{
if (string.IsNullOrEmpty(this.ProductName))
throw new Exception("ProductName cannot be empty");
}
protected override void BeforeInsert()
{
this.ProductUUID = Guid.NewGuid().ToString();
}
protected override void BeforeUpdate()
{
this.Total = this.Net + this.Tax;
}
protected override void AfterCommit()
{
DB.Update<ProductSales>()
.Set(ProductSales.ProductName).EqualTo(this.ProductName)
.Where(ProductSales.ProductId).IsEqualTo(this.ProductId)
.Execute();
}
}
}
In response to Dan's question:
First, have a look here: http://github.com/subsonic/SubSonic-2.0/blob/master/SubSonic/ActiveRecord/ActiveRecord.cs
In this file lives the whole logic I showed in my other post.
Validate: Is called during Save(), if Validate() returns false an exception is thrown.
Get's only called if the Property ValidateWhenSaving (which is a constant so you have to recompile SubSonic to change it) is true (default)
BeforeValidate: Is called during Save() when ValidateWhenSaving is true. Does nothing by default
BeforeInsert: Is called during Save() if the record is new. Does nothing by default.
BeforeUpdate: Is called during Save() if the record is new. Does nothing by default.
AfterCommit: Is called after sucessfully inserting/updating a record. Does nothing by default.
In my Validate() example, I first let the default ValidatColumnSettings() method run, which will add errors like "Maximum String lenght exceeded for column ProductName" if product name is longer than the value defined in the database. Then I add another errorstring if ProductName is empty and return false if the overall error count is bigger than zero.
This will throw an exception during Save() so you can't store the record in the DB.
I would suggest you call Validate() yourself and if it returns false you display the elements of this.Errors at the bottom of the page (the easy way) or (more elegant) you create a Dictionary<string, string> where the key is the columnname and the value is the reason.
private Dictionary<string, string> CustomErrors = new Dictionary<string, string>
protected override bool Validate()
{
this.CustomErrors.Clear();
ValidateColumnSettings();
if (string.IsNullOrEmpty(this.ProductName))
this.CustomErrors.Add(this.Columns.ProductName, "cannot be empty");
if (this.UnitPrice < 0)
this.CustomErrors.Add(this.Columns.UnitPrice, "has to be 0 or bigger");
return this.CustomErrors.Count == 0 && Errors.Count == 0;
}
Then if Validate() returns false you can add the reason directly besides/below the right field in your webpage.
If Validate() returns true you can safely call Save() but keep in mind that Save() could throw other errors during persistance like "Dublicate Key ...";
Thanks for the response, but can you confirm this for me as im alittle confused, if your validating the column (ProductName) value within validate() or the beforevalidate() is string empty or NULL, doesnt this mean that the insert / update has already been actioned, as otherwise it wouldnt know that youve tried to insert or update a null value from the UI / aspx fields within the page to the column??
Also, within asp.net insert or updating events we use e.cancel = true to stop the insert update, if beforevalidate failes does it automatically stop the action to insert or update?
If this is the case, isnt it eaiser to add page level validation to stop the insert or update being fired in the first place.
I guess im alittle confused at the lifecyle for these methods and when they come into play