How can I implement my own ClientStore in IdentityServer4? - c#

I wanna add some extra columns (e.g. ClientCustomProperty) to the Client entity in IdentityServer4, and deal with them in my business layer so I'm creating my custom Store like this:
public class MyClientStore : IClientStore
{
public Task<IdentityServer4.Models.Client> FindClientByIdAsync(string clientId)
{
// ...
}
}
I want to return my own Model with extra columns (not the IdentityServer4.Models.Client) from the Store, but the IClientStore.FindClientByIdeAsync signature is:
Task<IdentityServer4.Models.Client> FindClientByIdAsync(string clientId);
I think it should be something like this (Generic):
Task<TModel> FindClientByIdAsync<TModel>(string clientId)
where TModel: class, IClientModel /* IClientModel is in IS4 */
What do I need to do to get my custom model?

The suggestions in my comment are possible solutions. As long as you return a valid Client or Client-derived object to IS4 for FindClientByIdAsync(), you can store whatever you like against your Client.
Option 1: derive from Client:
public MyClient : Client
{
public string MyExtraProperty { get; set; }
}
Task<Client> FindClientByIdAsync(string clientId)
{
MyClient result = // fetch your client here;
return result;
}
Option 2: adapt to Client:
public MyClient
{
// Properties that Client requires, or can be adapted to what Client requires, here.
// ...
public string MyExtraProperty { get; set; }
}
Task<Client> FindClientByIdAsync(string clientId)
{
MyClient result = // fetch your client here;
return Adapt(result);
}
private Client Adapt(MyClient value)
{
return // your-client-adapted-to-Client here;
}
As Client already contains a lot of data, this option makes less sense than the others.
Option 3: add to Properties:
Here you add your additional data to the Client.Properties collection. IS4 will ignore it, but you can access the data wherever a Client instance is available. This option requires no custom type or even a custom IClientStore; it's already supported.

Related

Custom Attribute For Class Library Classes and Functions in C#

I'm developing 3rd party API connector bridge in class library NOT in ASP.NET.
User Levels
API has 3 user levels, lets say:
UserGoer
UserDoer
UserMaker
Service Restriction
Each API operation can work with one or multiple user level roles. For example, lets assume operations and reachable user levels as follows;
JokerService (reachable by UserGoer, UserMaker)
PokerService (reachable by UserGoer, UserDoer)
MokerService (reachable by UserGoer, UserDoer, UserMaker)
If UserDoer requests for JokerService, API returns bad request. JokerService is only reachable for UserGoer and UserMaker. So, I want to restrict and throw an exception.
User Token Structure
public interface IToken
{
string AccessToken { get; set; }
string RefreshToken { get; set; }
}
public class AuthenticationToken : IToken
{
[JsonProperty("access_token")]
public string AccessToken { get; set; }
[JsonProperty("refresh_token")]
public string RefreshToken { get; set; }
}
public class UserGoerAuthenticationToken : AuthenticationToken
{
}
public class UserDoerAuthenticationToken : AuthenticationToken
{
}
public class UserMakerAuthenticationToken : AuthenticationToken
{
}
Enum
public enum TokenType
{
Undefined = 0,
UserGoer = 1,
UserDoer = 2,
UserMaker = 3
}
Customized Authentication Attribute
public class AuthenticationFilter : Attribute
{
public TokenType[] TokenTypes { get; private set; }
public AuthenticationFilter(params TokenType[] TokenTypes)
{
this.TokenTypes = TokenTypes;
}
}
Example Service
[AuthenticationFilter(TokenType.UserGoer, TokenType.UserMaker)]
internal class JokerService : BaseService<JokerEntity>
{
public JokerService(IToken AuthenticationToken) : base(AuthenticationToken)
{
var tokenTypes =
(typeof(JokerService).GetCustomAttributes(true)[0] as AuthenticationFilter)
.TokenTypes;
bool throwExceptionFlag = true;
foreach (var item in tokenTypes)
{
// Check AuthenticationToken is UserGoer or UserMaker by StartsWith function
if (AuthenticationToken.GetType().Name.StartsWith(item.ToString()))
{
throwExceptionFlag = false;
break;
}
}
if (throwExceptionFlag)
throw new Exception("Invalid Authentication Token");
}
public JokerEntity Create(RequestModel<JokerEntity> model) => base.Create(model);
public JokerEntity Update(RequestModel<JokerEntity> model) => base.Update(model);
public JokerEntity Get(RequestModel<JokerEntity> model) => base.Get(model);
public List<JokerEntity> List(RequestModel<JokerEntity> model) => base.List(model);
}
In summary, JokerService can be executable by UserGoer and UserMaker. UserDoer has no permission for this service.
As you see the the usage of AuthenticationFilter attribute, I'm getting custom attributes in the constructor, because i want to know what IToken is. If there is an irrelevant "User Authentication Token" type that is passed as parameter (IToken), program should be throw an exception.
This is my solution, do you think is there any best practice for my problem?
Thank you for your help.
Interesting question. My initial thought at constructive critique would be that the tokens accepted by a particular class via the attribute is something decided at compile time and is unable to change. But, the checking for permissions is happening on the construction of each object.
You can prevent this with a static constructor that sets the tokenTypes variable. Static constructors always run before instance constructors. This is also a good place to ensure that tokenTypes is never null (in the absence of your custom attribute).
Likewise, the looping through tokenTypes can probably be a function that takes in an IToken and the tokenTypes, and more importantly, could probably live in the BaseService.cs. Writing that logic once will make it easier to maintain when some future requirement necessitates its change. :)
See also: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-constructors
Hope this helps.

Access Servicstack.net session in validator

How can I access a ServiceStack.net session in my validation code?
public class UserSettingsValidator : AbstractValidator<UserSettingsRequest>
{
public UserSettingsValidator()
{
RuleFor(x => x.UserId)
.SetValidator(new PositiveIntegerValidator())
.SetValidator(new UserAccessValidator(session.UserId)); //<-- I need to pass the UserID from the session here
}
}
In the Service Implementation I just do:
var session = base.SessionAs<UserSession>();
but this does not work for my abstract validator.
Thanks!
Edit: this is version 3.9.71.0
I assume you are just using the ValidationFeature plugin, as most do. If that's the case, then I don't think it is possible. Ultimately the ValidationFeature is a plugin which uses a RequestFilter.
I wanted to do something similar before too, then realised it wasn't possible.
The RequestFilter is run before the ServiceRunner. See the order of operations guide here.
What this means to you is your populated request DTO reaches your service, and the validation feature's request filter will try validate your request, before it has even created the ServiceRunner.
The ServiceRunner is where an instance of your service class becomes active. It is your service class instance that will be injected with your UserSession object.
So effectively you can't do any validation that relies on the session at this point.
Overcomplicated ?:
It is possible to do validation in your service method, and you could create a custom object that would allow you pass the session along with the object you want to validate. (See next section). But I would ask yourself, are you overcomplicating your validation?
For a simple check of the request UserId matching the session's UserId, presumably you are doing this so the user can only make changes to their own records; Why not check in the service's action method and throw an Exception? I am guessing people shouldn't be changing this Id, so it's not so much a validation issue, but more a security exception. But like I say, maybe your scenario is different.
public class SomeService : Service
{
public object Post(UserSettingsRequest request) // Match to your own request
{
if(request.UserId != Session.UserId)
throw new Exception("Invalid UserId");
}
}
Validation in the Service Action:
You should read up on using Fluent Validators. You can call the custom validator yourself in your service method.
// This class allows you to add pass in your session and your object
public class WithSession<T>
{
public UserSession Session { get; set; }
public T Object { get; set; }
}
public interface IUserAccessValidator
{
bool ValidUser(UserSession session);
}
public class UserAccessValidator : IUserAccessValidator
{
public bool ValidUser(UserSession session)
{
// Your validation logic here
// session.UserId
return true;
}
}
public class UserSettingsValidator : AbstractValidator<WithSession<UserSettingsRequest>>
{
public IUserAccessValidator UserAccessValidator { get; set; }
public UserSettingsValidator()
{
// Notice check now uses .Object to access the object within
RuleFor(x => x.Object.UserId)
.SetValidator(new PositiveIntegerValidator());
// Custom User Access Validator check, passing the session
RuleFor(x => x.Session).Must(x => UserAccessValidator.ValidUser(x));
}
}
Then to actually use the validator in your service:
public class SomeService : Service
{
// Validator with be injected, you need to registered it in the IoC container.
public IValidator<WithSession<UserSettingsRequest>> { get; set; }
public object Post(UserSettingsRequest request) // Match to your own request
{
// Combine the request with the current session instance
var requestWithSession = new WithSession<UserSettingsRequest> {
Session = this.Session,
Object = request
};
// Validate the request
ValidationResult result = this.Validator.Validate(requestWithSession);
if(!result.IsValid)
{
throw result.ToException();
}
// Request is valid
// ... more logic here
return result;
}
}
I hope this helps. Note: code is untested
It appears that after reading from a bunch of people experiencing similar problems, then many hours of playing with several solutions based on the SS4 Cookbook etc, this is a problem that is already solved:
https://forums.servicestack.net/t/blaz-miheljak-355-feb-3-2015/176/2
Implement the IRequiresRequest interface on your validator, and voila.

Exception when returning list of objects with servicestack

I am attempting to get ServiceStack to return a list of objects to a C# client, but I keep getting this exception:
"... System.Runtime.Serialization.SerializationException: Type definitions should start with a '{' ...."
The model I am trying to return:
public class ServiceCallModel
{
public ServiceCallModel()
{
call_uid = 0;
}
public ServiceCallModel(int callUid)
{
this.call_uid = callUid;
}
public int call_uid { get; set; }
public int store_uid { get; set; }
...... <many more properties> ......
public bool cap_expense { get; set; }
public bool is_new { get; set; }
// An array of properties to exclude from property building
public string[] excludedProperties = { "" };
}
The response:
public class ServiceCallResponse
{
public List<ServiceCallModel> Result { get; set; }
public ResponseStatus ResponseStatus { get; set; } //Where Exceptions get auto-serialized
}
And the service:
public class ServiceCallsService : Service
{
// An instance of model factory
ModelFactory MyModelFactory = new ModelFactory();
public object Any(ServiceCallModel request)
{
if (request.call_uid != 0)
{
return MyModelFactory.GetServiceCalls(request.call_uid);
} else {
return MyModelFactory.GetServiceCalls() ;
}
}
}
The client accesses the service with:
JsonServiceClient client = new ServiceStack.ServiceClient.Web.JsonServiceClient("http://172.16.0.15/");
client.SetCredentials("user", "1234");
client.AlwaysSendBasicAuthHeader = true;
ServiceCallResponse response = client.Get<ServiceCallResponse>("/sc");
The "model factory" class is a DB access class which returns a list. Everything seems to work just fine when I access the service through a web browser. The JSON returned from the service starts:
"[{"call_uid":70...."
And ends with:
"....false,"is_new":true}]"
My question is, what here might be causing serialization/deserialization to fail?
Solution
Thanks to the answer from mythz, I was able to figure out what I was doing wrong. My misunderstanding was in exactly how many DTO types there are and exactly what they do. In my mind I had them sort of merged together in some incorrect way. So now as I understand it:
Object to return (In my case, called "ServiceCallModel": The actual class you wish the client to have once ServiceStack has done its job. In my case, a ServiceCallModel is a key class in my program which many other classes consume and create.
Request DTO: This is what the client sends to the server and contains anything related to making a request. Variables, etc.
Response DTO: The response that the server sends back to the requesting client. This contains a single data object (ServiceCallModel), or in my case... a list of ServiceCallModel.
Further, exactly as Mythz said, I now understand the reason for adding "IReturn" to the request DTO is so the client will know precisely what the server will send back to it. In my case I am using the list of ServiceCallModel as the data source for a ListView in Android. So its nice to be able to tell a ListViewAdapter that "response.Result" is in fact already a useful list.
Thanks Mythz for your help.
This error:
Type definitions should start with a '{'
Happens when the shape of the JSON doesn't match what it's expecting, which for this example:
ServiceCallResponse response = client.Get<ServiceCallResponse>("/sc");
The client is expecting the Service to return a ServiceCallResponse, but it's not clear from the info provided that this is happening - though the error is suggesting it's not.
Add Type Safety
Although it doesn't change the behavior, if you specify types in your services you can assert that it returns the expected type, e.g Change object to ServiceCallResponse, e.g:
public ServiceCallResponse Any(ServiceCallModel request)
{
...
}
To save clients guessing what a service returns, you can just specify it on the Request DTO with:
public class ServiceCallModel : IReturn<ServiceCallResponse>
{
...
}
This lets your clients have a more succinct and typed API, e.g:
ServiceCallResponse response = client.Get(new ServiceCallModel());
instead of:
ServiceCallResponse response = client.Get<ServiceCallResponse>("/sc");
See the New API and C# Clients docs for more info.

Directly reference a method on DbContext using WCF Data Services?

Good afternoon fellow stackers (or overflowers, whichever you prefer), this is more of a cleanliness and convenience issue than anything else but I can't imagine that I'm the only one who's ever wondered about it so here we go...
I've got a basic OData enabled WCF Data Service class that's using my Entity Framework data context.
[JsonpSupportBehavior]
public class ControlBindingService : DataService<MyDataContext>
{
public static void InitializeService(DataServiceConfiguration config)
{
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;
config.DataServiceBehavior.AcceptCountRequests = true;
config.SetEntitySetAccessRule("*", EntitySetRights.All);
config.SetServiceOperationAccessRule("*", ServiceOperationRights.All);
}
protected override MyDataContext CreateDataSource()
{
if (HttpContext.Current == null)
throw new InvalidOperationException("The WCF Data Services implementation must be hosted in IIS.");
string username;
if (HttpContext.Current.User.Identity.IsAuthenticated)
username = HttpContext.Current.User.Identity.Name;
else
{
// The request didn't have user identity, attempt to find UserName in the
// request header before returning 401 to the caller.
if (!String.IsNullOrEmpty(HttpContext.Current.Request.Headers["UserName"]))
{
username = HttpContext.Current.Request.Headers["UserName"];
// REVIEW: We should validate user before passing it to the datacontext.
}
else
throw new DataServiceException(401, "Client did not pass required authentication information.");
}
return MyDataContext.GetInstance(username);
}
[WebGet]
public List<DailyKeyPerformanceIndicator> GetResourceKPIs(
int resourceId, string jsonStart, string jsonEnd, int scenarioId)
{
DateTime start = jsonStart.DeserializeJson<DateTime>();
DateTime end = jsonEnd.DeserializeJson<DateTime>();
if (scenarioId < 1)
{
scenarioId = CurrentDataSource.GetScenarios()
.Single(s => s.IsProduction).ScenarioID;
}
return CurrentDataSource.GetDailyResourceKPI(
scenarioId, start, end, resourceId);
}
}
The data context is just a standard (code-first) DbContext implementation with properties exposing the entity sets, etc..
However, we also have methods on there to expose some tables that we wanted to enforce some constraints upon. Specifically (see code below), we want to know what the caller wants to use the data for so we can return only the appropriate results. For example, if the caller wants to get rows from the employees table--they may want to get all rows, or only rows that they have update privileges for.
[Serializable]
public partial class MyDataContext : DbContext
{
static MyDataContext()
{
Database.SetInitializer<MyDataContext>(null);
}
public MyDataContext()
: base("name=MyDBString")
{ }
// Standard table properties...
public DbSet<User> Users
{
get { return this.Set<User>(); }
}
public DbSet<UserSetting> UserSettings
{
get { return this.Set<UserSetting>(); }
}
public DbSet<SettingDefinition> SettingDefinitions
{
get { return this.Set<SettingDefinition>(); }
}
// Restricted table methods...
public DbSet<Client> GetClients(
DatabasePermissions perms = DatabasePermissions.Select)
{
// getPermissibleSet is a method in a helper class that does some
// magical querying and produces a filtered DbSet.
return getPermissibleSet<Client>(perms);
}
public DbSet<Employee> GetEmployees(
DatabasePermissions perms = DatabasePermissions.Select)
{
// getPermissibleSet is a method in a helper class that does some
// magical querying and produces a filtered DbSet.
return getPermissibleSet<Employee>(perms);
}
}
Now to the root of the issue... What I'd like to avoid having to do is writing a [WebGet] for each and every "restricted table method" on my data context. The reason is really nothing more than redundancy--the [WebGet] method would end up being a direct pass-through to the data context.
So in summary, I'd say what I'm basically looking to do is to mark methods from my data context class that WCF will expose in the same way it does for my DbSet properties. Any takers?
Thanks! J
This is an interesting problem. I'm trying to do similar things. This is kind of throwing a dart here but have you tried something like this? You should probably separate the generics out so you don't create a unique context with each type, but it seems like you should be able to get rid of the duplicate code with generics.
[Serializable]
public partial class MyDataContext<T> : DbContext where T : class
{
static MyDataContext()
{
Database.SetInitializer<MyDataContext>(null);
}
public MyDataContext()
: base("name=MyDBString")
{ }
// Standard table properties...
public DbSet<T> SettingDefinitions
{
get { return this.Set<T>(); }
}
// Restricted table methods...
public DbSet<T> GetClients(
DatabasePermissions perms = DatabasePermissions.Select)
{
// getPermissibleSet is a method in a helper class that does some
// magical querying and produces a filtered DbSet.
return getPermissibleSet<T>(perms);
}
}

WCF Guid DataMember not Serialized properly

I have a WCF service that passes back and forth the following DataContracts:
[DataContract]
public class RequestWrapper
{
[DataMember]
public FooDataContract FooDataContract;
}
[DataContract]
public class ResponseWrapper
{
[DataMember]
public FooDataContract FooDataContract;
}
[DataContract]
public class FooDataContract
{
public FooDataContract(string data, Guid id)
{
Data = data;
ID = id;
}
[DataMember]
public string Data { get; set; }
[DataMember]
public Guid ID { get; set; }
}
It's called via a proxy class like this:
void CallService(string data)
{
var id = Guid.NewGuid();
var response = proxy.CallService(new RequestWrapper
{
new FooDataContract(data, id);
});
}
This is then passed (over the service) to the database via a repository using EF:
public void RepoMethod(FooDataContract foo)
{
var guid = foo.ID; // - Breakpoint here shows all zeros!
efContext.DoSomething(foo.Data, foo.ID);
}
Here's the service call:
public ResponseWrapper CallService(RequestWrapper request)
{
var foo = request.FooDataContract;
repository.RepoMethod(foo);
var response = new ResponseWrapper{ FooDataContract = foo };
return response;
}
Here's the proxy:
public class Proxy : IMyService
{
static readonly ChannelFactory<IMyService> channelFactory =
new ChannelFactory<IMyService>("IMyService");
ResponseWrapper CallService(RequestWrapper request)
{
return channelFactory.UseService(s => s.CallService(request));
}
}
internal static class UseServiceFunction
{
internal static R UseService<T, R>
(this ChannelFactory<T> channelFactory, Func<T, R> useService)
{
var service = channelFactory.CreateChannel();
try
{
R response = useService(service);
return response;
}
finally
{
var channel = service as ICommunicationObject;
try
{
if (channel.State != CommunicationState.Faulted) channel.Close();
}
catch { channel.Abort(); }
}
}
}
I've put a watch on the Guid in the VS debugger. When the service is called from a client web application, the generated Guid is a valid Guid of seemingly random hex characters. Great, that's working.
But when the data is serialized, goes over the wire, and comes out the other side (in my repository), the Guid is all zeros!
I've double, triple checked that the Guid is indeed marked with the [DataMember] attribute. I'm wondering if the extra layer of DataContract (how a FooDataContract is wrapped with the RequestWrapper data contract) is causing a serialization issue?
I think your problem here is that the constructor you've made in your DataContract class doesn't get passed to the proxy on the client side. WSDL won't know anything about this. Think of your data contracts as just a place to stick data with no other functionality. To confirm, you can look in the reference.cs class that got generated in the client when you added the service reference.
I'd suggest re-writing the code so that you explicitly set each of the values in your data contract rather than relying on the constructor.
You can also write a hand coded proxy that has whatever behavior you want and then share that file with the client. That would work, but then you'll be more tightly coupling your client to your service.
Turns out, my translation layer wasn't updated to convert between the DTOs! Whooooops!

Categories