In this section of docs not all use-cases of guard usage explained clearly:
NestJS Docs - Claims-based authorization
CaslAbilityFactory implemented for these use-cases:
Admins can manage (create/read/update/delete) all entities
Users have read-only access to everything
Users can update their articles (article.authorId === userId)
Articles that are published already cannot be removed (article.isPublished === true)
and explained only the most trivial use-case:
Users have read-only access to everything
It's demonstrated with this controller method:
#Get()
#UseGuards(PoliciesGuard)
#checkPolicies((ability: AppAbility) => ability.can(Action.Read, Article))
findAll() {
return this.articlesService.findAll();
}
but how should I annotate a method to check the 3rd or 4th use-cases:
Articles that are published already cannot be removed:
(article.isPublished === true)
#Delete()
#UseGuards(PoliciesGuard)
#checkPolicies(?????????????????????????????)
delete(#Body() article: Article) {
return this.articlesService.delete(article.id);
}
Is it possible, at all? For this requirement PoliciesGuard or handler declared in #checkPolicies should be able to access method arguments.
How can controller method arguments be accessed from a guard?
Of course a workaround solution if you call ability.can(...) directly from controller method:
#Delete()
#UseGuards(SomeGuards but NOT PoliciesGuard)
delete(#Body() article: Article) {
const ability = this.caslAbilityFactory.createForUser(<<user from request>>);
if (!ability.can(Action.Delete, article)) {
throw new UnauthorizedException();
}
return this.articlesService.delete(article.id);
}
But this solution doesn't fit the original declarative pattern.
You can achieve this in the PolicyGuard. This is mentioned in NestJS docs here
your policy guard will be like this
#Injectable()
export class PoliciesGuard extends RequestGuard implements CanActivate {
public constructor(private reflector: Reflector, private caslAbilityFactory: CaslAbilityFactory) {
super();
}
public async canActivate(context: ExecutionContext): Promise<boolean> {
const policyHandlers = this.reflector.get<PolicyHandler[]>(CHECK_POLICIES_KEY, context.getHandler()) || [];
const request = this.getRequest(context);
const { user } = request;
const ability = await this.caslAbilityFactory.createForUser(user?.userId);
return policyHandlers.every(handler => this.execPolicyHandler(handler, ability, request));
}
private execPolicyHandler(handler: PolicyHandler, ability: AppAbility, request: Request) {
if (typeof handler === 'function') {
return handler(ability, request);
}
return handler.handle(ability, request);
}
}
then the checkPolicy will accept this function
export class ReadArticlePolicyHandler implements IPolicyHandler {
handle(ability: AppAbility, request) {
const { query } = request;
const article = new Article();
article.scope = query.scope;
return ability.can(Action.Read, article) || ability.can(Action.Delete, article);
}
}
Related
I am playing around with asyncapi/modelina CSharpGenerator. I would like to add inheritance to the generated class something like this
public class UserCreated: IEvent
{
}
Is that possible? Can we add additional dependencies other than the generated ones?
Inheritance is, unfortunately, one of those features that have gotten put on the backburner, and still is.
Fortunately, it is possible to accomplish it, but it does require you to overwrite the entire rendering behavior, which might not be maintainable in the long run. You can find the full example in this PR: https://github.com/asyncapi/modelina/pull/772
const generator = new CSharpGenerator({
presets: [
{
class: {
// Self is used to overwrite the entire rendering behavior of the class
self: async ({renderer, options, model}) => {
//Render all the class content
const content = [
await renderer.renderProperties(),
await renderer.runCtorPreset(),
await renderer.renderAccessors(),
await renderer.runAdditionalContentPreset(),
];
if (options?.collectionType === 'List' ||
model.additionalProperties !== undefined ||
model.patternProperties !== undefined) {
renderer.addDependency('using System.Collections.Generic;');
}
const formattedName = renderer.nameType(model.$id);
return `public class ${formattedName} : IEvent
{
${renderer.indent(renderer.renderBlock(content, 2))}
}`;
}
}
}
]
});
What is happening here is that we create a custom preset for the class renderer and overwrite the entire rendering process of itself.
This will generate based on this input:
public class Root : IEvent
{
private string[] email;
public string[] Email
{
get { return email; }
set { email = value; }
}
}
Regarding dependencies, please see https://github.com/asyncapi/modelina/blob/master/docs/presets.md#adding-new-dependencies. You can do this in the self preset hook.
You can read more about the presets here: https://github.com/asyncapi/modelina/blob/master/docs/presets.md
How can I pass the decoded Data from my Api to my GetX Controller?
Here is my Class "Germany" and my fetchGermany() Function.
Future<Germany> fetchGermany() async {
final response =
await get(Uri.parse('https://api.corona-zahlen.org/germany'));
if (response.statusCode == 200) {
return Germany.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to get data');
}
}
class Germany {
int cases;
int deaths;
int recovered;
double weekIncidence;
double casesPer100k;
int casesPerWeek;
Germany(
{required this.cases,
required this.deaths,
required this.recovered,
required this.weekIncidence,
required this.casesPer100k,
required this.casesPerWeek});
factory Germany.fromJson(Map<String, dynamic> json) {
return Germany(
cases: json["cases"],
deaths: json["deaths"],
recovered: json["recovered"],
weekIncidence: json["weekIncidence"],
casesPer100k: json["casesPer100k"],
casesPerWeek: json["casesPerWeek"]);
}
}
Here is my GetX controller which is empty at the moment:
class DetailController extends GetxController {
}
So basically I just want to be able to acceess this data:
cases: json["cases"],
deaths: json["deaths"],
recovered: json["recovered"],
weekIncidence: json["weekIncidence"],
casesPer100k: json["casesPer100k"],
casesPerWeek: json["casesPerWeek"]
While I agree with #DarShan that you don't necessarily need a GetXController here, I still would just for the simple sake of using a stateless widget over a stateful widget. If for no other reason than less cluttered UI code and separating business logic.
Also not sure if your Api call function is global or if that's just how you have it in your example, but if it is global I'd create a helper class.
class ApiHelper {
Future<Germany> fetchGermany() async {
final response =
await get(Uri.parse('https://api.corona-zahlen.org/germany'));
if (response.statusCode == 200) {
return Germany.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to get data');
}
}
}
Then your GetX class can look like this.
class DetailController extends GetxController {
Germany germany;
#override
void onInit() async {
super.onInit();
final apiHelper = ApiHelper();
germany = await apiHelper.fetchGermany();
}
}
And here's an example using GetView widget which is just a stateless widget with a built in controller of the type you provided without having to find it.
class GermanyExample extends GetView<DetailController> {
#override
Widget build(BuildContext context) {
// access the initialized Germany object with controller.germany
return // the rest of your UI
}
}
Why not directly use the returned Germany object?
I don't see a need to use GetxController here.
Can be simply used as:
Germany _germany;
#override
void initState() {
super.initState();
fetchGermanyData();
}
fetchGermanyData() async {
final fetchedData = await fetchGermany();
setState(() => _germany = fetchedData);
}
/// use ? : operator to show relevant UI in the build method.
In a regular type scenario, where a Route is available, say to only "Premium" users, ocelot.global.json would have RouteClaimsRequirement like this:
"RouteClaimsRequirement" : { "Role" : "Premium" }
This would get translated to a KeyValuePair<string, string>(), and it works nicely.
However, if I were to open a route to 2 types of users, eg. "Regular" and "Premium", how exactly could I achieve this?
I found a way through overriding of default Ocelot middleware. Here are some useful code snippets:
First, override the default AuthorizationMiddleware in Configuration() in Startup.cs:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
var config = new OcelotPipelineConfiguration
{
AuthorisationMiddleware
= async (downStreamContext, next) =>
await OcelotJwtMiddleware.CreateAuthorizationFilter(downStreamContext, next)
};
app.UseOcelot(config).Wait();
}
As you can see, I am using a custom OcelotJwtMiddleware class up there. Here is that class, pasted:
public static class OcelotJwtMiddleware
{
private static readonly string RoleSeparator = ",";
public static Func<DownstreamContext, Func<Task>, Task> CreateAuthorizationFilter
=> async (downStreamContext, next) =>
{
HttpContext httpContext = downStreamContext.HttpContext;
var token = httpContext.Request.Cookies[JwtManager.AuthorizationTokenKey];
if (token != null && AuthorizeIfValidToken(downStreamContext, token))
{
await next.Invoke();
}
else
{
downStreamContext.DownstreamResponse =
new DownstreamResponse(new HttpResponseMessage(HttpStatusCode.Unauthorized));
}
};
private static bool AuthorizeIfValidToken(DownstreamContext downStreamContext, string jwtToken)
{
IIdentityProvider decodedObject = new JwtManager().Decode<UserToken>(jwtToken);
if (decodedObject != null)
{
return downStreamContext.DownstreamReRoute.RouteClaimsRequirement["Role"]
?.Split(RoleSeparator)
.FirstOrDefault(role => role.Trim() == decodedObject.GetRole()) != default;
}
return false;
}
}
JwtManager class here is just my small utility made using the default Jwt NuGet package, nothing special. Also, JWT is being stored as a Cookie, which is not safe, but doesn't matter here. If you happen to copy paste your code, you will have small errors relating to this, but just switch it out with your own implementations of auth tokens.
After these 2 snippets were implemented, ocelot.global.json can have RouteClaimsRequirement such as this:
"RouteClaimsRequirement" : { "Role" : "Premium, Regular" }
This will recognize both clients with Regular in their Cookies, as well as those with Premium.
I am using the BLOC pattern for my latest Flutter app and I started out using something like this for my output streams:
class MyBloc {
// Outputs
final Stream<List<Todo>> todos;
factory MyBloc(TodosInteractor interactor) {
final todosController = BehaviorSubject<List<Todo>>()
..addStream(interactor.todos);
return MyBloc._(todosController);
}
MyBloc._(this.todos);
}
but slowly I found myself doing something more like this, using a method (or getter) after awhile:
class MyBloc {
final TodosInteractor _interactor;
// Outputs
Stream<List<Todo>> todos(){
return _interactor.todos;
}
MyBloc(this._interactor) { }
}
For people who want to see... getter for todos in TodosInteractor:
Stream<List<Todo>> get todos {
return repository
.todos()
.map((entities) => entities.map(Todo.fromEntity).toList());
}
When I look at the differing code, I see that the first example uses a field versus a method to expose the stream but I couldn't figure out why I would choose one over the other. It seems to me that creating another controller just to push through the stream is a little much... Is there a benefit to this other than being immutable in my todos stream definition? Or am I just splitting hairs?
Well maybe this will not be a best answer but it is a good practice expose your output stream using get methods. Below a example of a bloc class that i have written to a project using RxDart.
class CityListWidgetBloc {
final _cityInput = PublishSubject<List<Cidade>>();
final _searchInput = new PublishSubject<String>();
final _selectedItemsInput = new PublishSubject<List<Cidade>>();
// exposing stream using get methods
Observable<List<Cidade>> get allCities => _cityInput.stream;
Observable<List<Cidade>> get selectedItems => _selectedItemsInput.stream;
List<Cidade> _searchList = new List();
List<Cidade> _selectedItems = new List();
List<Cidade> _mainDataList;
CityListWidgetBloc() {
//init search stream
_searchInput.stream.listen((searchPattern) {
if (searchPattern.isEmpty) {
_onData(_mainDataList); // resend local data list
} else {
_searchList.clear();
_mainDataList.forEach((city) {
if (city.nome.toLowerCase().contains(searchPattern.toLowerCase())) {
_searchList.add(city);
}
});
_cityInput.sink.add(_searchList);
}
});
}
//getting data from firebase
getCity( {#required String key}) {
FirebaseStateCityHelper.getCitiesFrom(key, _onData);
//_lastKey = key;
}
searchFor(String pattern) {
_searchInput.sink.add(pattern);
}
void _onData(List<Cidade> list) {
_mainDataList = list;
list.sort((a, b) => (a.nome.compareTo(b.nome)));
_cityInput.sink.add(list);
}
bool isSelected(Cidade item) {
return _selectedItems.contains(item);
}
void selectItem(Cidade item) {
_selectedItems.add(item);
_selectedItemsInput.sink.add(_selectedItems);
}
void selectItems(List<Cidade> items){
_selectedItems.addAll( items);
_selectedItemsInput.sink.add( _selectedItems );
}
void removeItem(Cidade item) {
_selectedItems.remove(item);
_selectedItemsInput.sink.add(_selectedItems);
}
dispose() {
_cityInput.close();
_searchInput.close();
_selectedItemsInput.close();
}
}
I use to change dynamic child component in body and keep static header, bottom and menu.
My problem: When use BehaviorSubject as shared-data between components, then UI (*ngFor) not be updated event shared-data transferred well. I am using Angular 5.2.0, RxJs 5.5.6
My app has flow:
user click search button on Layout-top.component.ts -> fetch data from Backend server by Home.service.ts-> set data in BehaviorSubject object.
On Home.component.ts constructor always subscribe shared-data from Home.service.ts -> change data of Home.component.ts -> display them.
1. App.compoenet.ts
#Component({
selector: 'xxx',
template:
`
<gotop position="200"></gotop>
<layout-top></layout-top>
<router-outlet></router-outlet>
<layout-bottom></layout-bottom>
`
})
export class AppbComponent implements OnInit, AfterViewInit{
public ngAfterViewInit(): void {
this.spinner.hide();
}
message:string;
constructor(private spinner:Spinner){
}
public ngOnInit(){
this.spinner.show();
}
}
Layout-top.component.ts
public doSearch(){
let filter = {
xx:'XXX'
};
this.homeService.setData(filter);
}
3.Home.service.ts
#Injectable()
export class HomeService extends BaseService{
public data =new BehaviorSubject<DataType>(<DataType>{});
public eventFilter: EventEmitter<{}> = new EventEmitter();
public constructor(private http: HttpClient,
private _const: Const,
private util:Util,
private appref: ApplicationRef) {
super(_const, util);
}
public listProduct(filter):Observable<any>{
const url = url to my backend api
let headers:HttpHeaders = this.util.header(this._const, null, 'application/json');
return this.http.post(
url,
filter,
{headers})
.map(res => {
return res;
});
}
public getData():Observable<DataType>{
return this.data.asObservable();
}
public setData(filter:any):void {
const listProduct$ = this.listProduct(filter);
listProduct$.subscribe(res => {
this.data.next({res:res, filter:filter});
});
}
public cleanData() {
this.data.next(null);
}
}
layout-top.html
5.home.html
<div class="product-item"
*ngFor="let item of listProducts">
<!--display some thing here-->
</div>
6.home.component.ts
constructor(private service: HomeService,
private cdRef:ChangeDetectorRef,
private zone:NgZone,private appref: ApplicationRef ){
this.subsListProduct = this.service.getData().subscribe(obj=>{
this.zone.run(()=>{
$("#in-blur").css("display", "block");
if(!obj){
return;
}
const res = obj.res;
const filter = obj.filter;
if(res && filter){
this.listProducts = res.list;
this.cdRef.detectChanges();
}
});
setTimeout(()=>{
$("#in-blur").css("display", "none");
}, 1000);//for test loading spinner. will be remove in product
});
}
"this.listProducts = res.list;" work fine, ther listProducts be updated, but UI is not any change.
Many people advised use zone.run() or ChangeDetectorRef.detectChanges() but not work in my app. Plz support me.
The best way to map observable data to views is to use the async pipe - https://angular.io/api/common/AsyncPipe - this way all the change management and unsubscribing form the observable is handled for you. So in your example:
The view:
<div class="product-item"
*ngFor="let item of (listProducts | async)?.list">
<!--display some thing here-->
</div>
The ? before .list makes the property optional, so nothing will break if list is is null or undefined
The home component:
constructor(private service: HomeService,
private appref: ApplicationRef ){
this.listProducts = this.service.getData();
}
If you need to manipulate any of the data from the observable before displaying in the view do that in a .map, eg.
constructor(private service: HomeService,
private appref: ApplicationRef ){
this.subsListProduct = this.service.getData()
.map(obj => {
if (obj.list && obj.filter) {
return obj;
} else {
return null;
}
});
}
You should also consider dropping jquery for doing your css changes and use ngClass in your view to do this instead - https://angular.io/api/common/NgClass