list the api in swagger based on user roles - api

I have a requirement where I want to list the api methods in swagger based on user roles.
For example :-
User A with basic access can use limited api methods.
User B with Admin access can use all the listed api methods.
I don't know how to achieve this.
I am using Swashbuckle.AspNetCore Version="1.0.0"

Possible solution:
Define several dockets in your swagger config with different group names
#Bean
public Docket api1() {
...
return new Docket(DocumentationType.SWAGGER_2)
...
.groupName("api1")
...
.paths(PathSelectors.ant("/api/api1Url/**"))
.build().apiInfo(metaData());
}
#Bean
public Docket api2() {
...
return new Docket(DocumentationType.SWAGGER_2)
...
.groupName("api2")
...
.paths(PathSelectors.ant("/api/api2Url/**"))
.build().apiInfo(metaData());
}
Define your own DocumentationCache which overrides Swagger's one
#Primary
#Component
public class RolesAwareDocumentationCache extends DocumentationCache {
private final Map<String, Set<String>> allowedResourcesPerRole =
Map.of(SecurityConfig.API1_ROLE, Collections.singleton("api1"),
SecurityConfig.API2_ROLE, Collections.singleton("api2"),
SecurityConfig.SOME_ADMIN_ROLE, Set.of("api1", "api2"));
#Override
public Map<String, Documentation> all() {
var documentationMap = super.all();
return documentationMap.entrySet().stream()
.filter(e -> isAllowedForRole(e.getKey())) // check if has access to this group
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
private boolean isAllowedForRole(String groupName) {
var userAuthorities = SecurityContextHolder.getContext().getAuthentication().getAuthorities().stream()
.map(Object::toString)
.collect(Collectors.toUnmodifiableSet());
return userAuthorities.stream()
.map(allowedResourcesPerRole::get) // get allowed resources for all of the user roles
.filter(Objects::nonNull)
.flatMap(Collection::stream) // flatMap to collection
.anyMatch(s -> s.contains(groupName)); // check if result collection has specified group name
}
}
So this cache will return groups based on the current user's role from the security context. You can actually use any rules to restrict access to different groups.
Also do not forget to define proper permissions for HttpSecurity to restrict the invocation of an API for not allowed roles.

Try using an IDocumentFilter, you can limit what the user gets in the SwaggerDocument and the swagger-ui feeds from that.
Here are some examples https://github.com/heldersepu/SwashbuckleTest/blob/master/Swagger_Test/App_Start/SwaggerConfig.cs#L261

Related

DDD: Can I pass a Domain-Service as a parameter of aggregate constructor

I have a domain that maintains accounts for other systems(Media).
At first, I derived the following aggregate root
public class Account extends Entity {
private AccountId accountId;
private TenantId tenantId;
private LoginAccount loginAccount;
private Media media;
private LoginValidity validity;
public Account(TenatId shopId, Media media, LoginAccount loginAccount) {
this.accountId = new AccountId();
this.setTenatId(shopId);
this.set(media);
this.setLoginValidity(LoginValidity.NOT_REQUESTED);
}
public void validateLogin(LoginValidationService loginValidationService) {
LoginValidity validity = loginValidationService.validateLoginFor(media,loginAccount);
setLoginValidity(validity);
//TO-DO EVENT PUBLISHING
}
public void changeLoginAccount(LoginAccount loginAccount) {
setLoginAccount(loginAccount);
setLoginValidity(LoginValidity.NOT_REQUESTED);
//TO-DO EVENT PUBLISHING?
}
...Setters
}
And I also derived the LoginValidationService as a Domain-Service.
LoginValidationService determines strategy(policy) using Media
And then I also derive two business logic(invariant)
When the User adds a Account login validation must occur.
When the User changes LoginAccount login validation must occur.
My question is that for the first invariant,LoginValidationService(A Domain-Service) could be a parameter for aggregate root's constructor like this
public class AccountApplicationService {
private LoginValidationService loginValidationService;
private AccountRepository accountRepository;
public Account createAccount(CreateAccountCommand command) {
TenantId tenantId = new TenantId(command.getTenantId());
Media media = mediaService.mediaFrom(command.getMediaId());
Account account = new Account(tenantId,
media,
command.getLoginAccount(),
loginValidationService);
accountRepository.save(account);
return ccount;
}
...
}
public class Account extends Entity {
private AccountId accountId;
private TenantId tenantId;
private LoginAccount loginAccount;
private Media media;
private LoginValidity validity;
public Account(TenatId shopId,
Media media,
LoginAccount
loginAccount,LoginValidationService loginValidationService) {
this.accountId = new AccountId();
this.setTenatId(shopId);
this.set(media);
LoginValidity validity =
loginValidationService.validateLoginFor(media,loginAccount);
this.setLoginValidity(validity);
}
....
}
Is exist the Pattern above? (passing the domain-service to the constructor) and is it the right approach of DDD?
Do I have to derive the first invariant as a use-case? like this,
public class AccountApplicationService {
private LoginValidationService loginValidationService;
private AccountRepository accountRepository;
public Account createAccountAndValidateLogin(CreateAccountAndValidateLoginCommand command) {
TenantId tenantId = new TenantId(command.getTenantId());
Media media = mediaService.mediaFrom(command.getMediaId());
MediaAccount mediaAccount = new MediaAccount(tenantId,media,command.getLoginAccount());
mediaAccount.validateLogin(loginValidationService);
mediaAccountRepository.save(mediaAccount);
}
...
}
Please give me any advice.
-----------Edit---------
I add the Account's constructor code. LoginValidationService is not a member of Account.
Would be a solution for you to pass the LoginValidity as Account aggregate constructor parameter?
public Account createAccount(CreateAccountCommand command) {
TenantId tenantId = new TenantId(command.getTenantId());
Media media = mediaService.mediaFrom(command.getMediaId());
LoginValidity loginValidity =
loginValidationService.validateLoginFor(media,command.getLoginAccount());
Account account = new Account(tenantId,
media,
command.getLoginAccount(),
loginValidity);
accountRepository.save(account);
return ccount;
}
I think that to validate an account is something that the entity cannot do by yourself so it is clear is domain service's responsibility, then I would see in the use case flow logic, or you can create the account with a domain service responsible of validate and create the account and you had the business logic encapsulates in domain service instead of use case.
Can I pass a Domain-Service as a parameter of aggregate constructor
Yes, but I would expect that to make your life harder in the long run.
An AGGREGATE is a cluster of associated objects that we treat as a unit for the purpose of data changes. -- Evans, 2003
Domain Services, however, don't change -- they are stateless, and "any client can use any instance of a particular SERVICE without regard to the instance's individual history."
So mixing the patterns in this way is a bit odd on two fronts; including the service as a member of the aggregate suggests that it changes, and also it implies that this aggregate has a special relationship with a particular instance of the service, which contradicts the notion that they are not interchangeable.
It's not clear to me what compensating advantage you get in a design where the service is embedded within your aggregate.
I found an additional invariant for Account creating that Account can't hold itself so I derived AccountProvisionService(Domain-Service). The Application-Service code is as follows.
...
public Account createAccount(CreateAccountCommand command) {
return AccountProvisioningService.provisionAccount(
command.getTenantId(), command.getMediaId(), command.getLoginAccount());
}
public void changeLoginAccount(ChangeLoginAccountCommand command) {
Account account = existingAccount(command.getShopId(),command.getAccountId());
LoginValidity loginValidity = loginValidationService.validateLoginFor(Account.media(), command.getLoginAccount());
account.changeLoginAccount(command.getLoginAccount(),loginValidity);
AccountRepository.save(account);
}
...

IAuthorizationPolicyProvider: can I use multiple policy providers or I have to consider all cases inside one general policy provider?

In my program I want to
Check if users are registered in a certain users table for all possible actions.
Just for certain actions, I want also to check if the user has the appropriate CRUD rights.
I check the 1st condition in startup with the following code:
services.AddAuthorization(options =>
{
// This policy checks if a user is registered in our Users table.
options.AddPolicy(
"UserIsRegistered",
new AuthorizationPolicyBuilder()
.AddRequirements(new RegistrationRequirement())
.Build());
});
I also add
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers().RequireAuthorization("UserIsRegistered");
});
and I define the RegistrationRequirement and the RegistrationAuthorizationHandler accordingly.
For the 2nd condition, I created an attribute CrudAuthorizeAttribute with its CrudAuthorizationHandler. Since I have one different police for each possible Crud action, I created a CrudPolicyProvider.
Then I registered everything in Startup:
services.AddTransient<IAuthorizationHandler, RegistrationAuthorizationHandler>();
services.AddTransient<IAuthorizationHandler, CrudAuthorizationHandler>();
services.AddTransient<IAuthorizationPolicyProvider, CrudPolicyProvider>();
Now I see when I am checking the 1st condition I'm getting the policy from CrudPolicyProvider but I expect this policy generator to provide policies only in the 2nd condition.
Therefore:
Is there any way to specify that CrudPolicyProvider has to be used only with the CrudAttribute and nowhere else?
More generally, can I define two policy providers (as I can do with IAuthorizationHandler)
services.AddTransient<IAuthorizationPolicyProvider, FirstPolicyProvider>();
services.AddTransient<IAuthorizationPolicyProvider, SecondPolicyProvider>();
and use each one only when required?
If the answer is no, does it mean that I have to define a single GeneralPolicyProvider and inside this provider check which policies I have to provide each time depending on the situation I am? (something like using the POLICY_PREFIX here?).
Thanks!
can I define two policy providers (as I can do with IAuthorizationHandler)and use each one only when required
You could. But the two separated policy providers won't be activated correspondingly. You might have multiple policy providers, but only one of them will be used at the same time. Also there's no magic when using IAuthorizationPolicyProvider.
define a single GeneralPolicyProvider .... check which policies I have to provide each time depending on the situation I am? (something like using the POLICY_PREFIX here?).
Yes. But you don't have to use Policy_Prefix everywhere. In that way you'll repeat yourself too many times.
A better way is to inject a IHttpContextAccessor service into GeneralPolicyProvider, such that you can check the current EndPoint/HttpContext at run-time, and then you can resolve the target Policy Provider dynamically.
An implementation looks like:
public class GenericPolicyProvider : IAuthorizationPolicyProvider
{
private readonly IHttpContextAccessor _httpContextAccessor;
private AuthorizationOptions _authZOpts { get; }
public DefaultAuthorizationPolicyProvider FallbackPolicyProvider { get; }
private IAuthorizationPolicyProvider _fstPolicyProvider {get;set;}
private IAuthorizationPolicyProvider _sndPolicyProvider {get;set;}
public GenericPolicyProvider(IHttpContextAccessor httpContextAccessor, IOptions<AuthorizationOptions> options)
{
this._httpContextAccessor = httpContextAccessor;
this._authZOpts = options.Value;
this.FallbackPolicyProvider = new DefaultAuthorizationPolicyProvider(options);
this._fstPolicyProvider = new FirstPolicyProvider(options.Value);
this._sndPolicyProvider = new SecondPolicyProvider(options.Value);
}
// use the target provider to provide policy
public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
{
var targetPolicyProvider = this.GetPolicyProvider(policyName);
return targetPolicyProvider.GetPolicyAsync(policyName);
}
// get the target provider dynamically
private IAuthorizationPolicyProvider GetPolicyProvider(string policyName)
{
var httpContext = this._httpContextAccessor.HttpContext;
if(httpContext==null) throw new Exception("HttpContext must not be null");
// now you get the HttpContext
// check HttpContext to determine which policy provider should be used
// ...
// or check endpoint,e.g. get a mark filter by endpoint.Metadata.GetMetadata<...>()
var endpoint = httpContext.GetEndpoint();
var someMarker = endpoint.Metadata.GetMetadata<SomeMarker>();
// in short, resolve the policy provider dynamically:
if(shouldUseFirstPolicyProvider())
return this._fstPolicyProvider;
else if(shouldUseSecondPolicyProvider())
return this._sndPolicyProvider;
else
return this.FallbackPolicyProvider;
}
...
}
Finally, don't forget to register this GenericPolicyProvider in Startup.

Extending Restlet 2.3 ClientInfo

Is it possible to extend org.restlet.data.ClientInfo? I need a convenient way of adding a List<String> permissions to complement the existing List<Role> roles. In a perfect world I would be able to add List<Permission> permissions but the former is perfectly acceptable.
I need to be able to get this from the request: org.restlet.resource.Resource.getRequest().getClientInfo().getPermissions()
I don't think that it's possible to add something within the class ClientInfo since it's a class that is managed by the Restlet engine. You can't subclass it to add a field permissions (you don't have the hand on the client info instantiation).
That said, you can leverage the context attributes. I mean that you can fill within your Enroler implementation an attribute permissions for the request, as described below:
public class MyEnroler implements Enroler {
private Application application;
public MyEnroler(Application application) {
this.application = application;
}
public void enrole(ClientInfo clientInfo) {
// Roles
Role role = new Role(application, "roleId",
"Role name");
clientInfo.getRoles().add(role);
// Permissions
Request request = Request.getCurrent();
List<Permission> permissions = new ArrayList<Permission>();
request.getAttributes().put("permissions", permissions);
Permission permission = (...)
permissions.add(permission);
}
Hope it helps you,
Thierry

ServiceStack authentication with both [Authenticate] and [ValidateApiKey] attributes

I have some endpoints decorated with the [Authenticate] attribute. Now a third party client has to access the same endpoint by using a shared API key.
As the code would be exactly the same for the two cases, I would like to check first if the current request comes from an authenticated user and, if not, checks as fallback if a valid API key is provided.
Is there a way to use both [Authenticate] and [ValidateApiKey] attributes for the same endpoint?
Something like:
[Authenticate | ValidateApiKey]
public long Post(MyDto request)
{
// ....
}
Attributes can only be combined to add functionality, i.e. they can't be used as a fallback or a switch. To get the desired behavior your [ValidateApiKey] attribute should perform the validation fallback as part of its implementation, e.g:
public class ValidateApiKeyAttribute : RequestFilterAttribute
{
public override void Execute(IRequest req, IResponse res, object reqDto)
{
var session = req.GetSession();
if (session == null || !session.IsAuthenticated)
{
//If not a valid key, execute the `[Authenticate]` attribute
//to handle failed response
if (!CheckValidApiKey(req))
new AuthenticateAttribute().Execute(req,res,reqDto);
}
}
}
Note: Responses should be reference types (e.g. DTO's) or raw strings not value types.
public object Post(MyDto request)
{
// ....
}

Role-based access control with Spring MVC

I would like to know the best practices for the role based access control with spring.
My requirements are,
I will have set of roles assigned to users say,
user1=admin, user2=expert
user1 will have the accesses write like
/admin/member-management
/admin/project-management
......
for user2....
/myproject1/*
so if user2 tries to access the url
/admin/member-management
will be redirect to authorization failure page.
The standard framework to use with Spring MVC is Spring Security. While it can be very complex, here's a minimal version of what you need: 4.2.2 A Minimal Configuration
In your case, the config would be something like this:
<http auto-config='true'>
<intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
</http>
Spring Security has the concept of roles but out of the box it does not have a concept of permissions. It does have a concept of ACLs but this ACLs are a lot more complicated than permissions, and they are tied to acting on specific objects, versus authorizing actions in general.
Take a look at Apache Shiro. It has roles and permissions that look very similar to what you gave as an example (using wildcards). It is also easy to use with Spring.
public class DashBoardController {
#Autowired
UserService userService;
private static final Logger logger = LoggerFactory.getLogger(DashBoardController.class);
#SuppressWarnings("unchecked")
#RequestMapping(value = PathProxy.DashBoardUrls.SHOW_DASHBOARD, method = RequestMethod.GET)
public String role(Locale locale, Model model) {
String userRole = null;
logger.info("dashboard Controller");
Collection<SimpleGrantedAuthority> authorities = (Collection<SimpleGrantedAuthority>) SecurityContextHolder
.getContext().getAuthentication().getAuthorities();
for (SimpleGrantedAuthority simpleGrantedAuthority : authorities) {
userRole = simpleGrantedAuthority.toString();
}
switch (userRole) {
case "ROLE_ADMIN":
return "dashboard/admin";
case "ROLE_HR_MANAGER":
return "dashboard/hr_manager";
case "ROLE_MANAGER":
return "dashboard/manager";
case "ROLE_EMPLOYEE":
return "dashboard/employee";
case "ROLE_COMPANY_ADMIN":
return "dashboard/admin";
default:
break;
}
return userRole;
}
}