I would like to block URL access from unauthorized users. I'm currently using canActivate with route Guard from angular to block users to access /my/specific_url_route.
Indeed, I want to block some users (not listed in a list on my database) from accessing the resource by typing the matching URL (like: mydomain/legalForm/123ytfdhs653HG). To do that, first of all, I already block the access from my user view (OK) but the unauthorized users can still access the resource (page) by typing the correct URL directly (the subject).
My code is correct (return false and true as expected on my service) but my guard
canActivate()
is executed and returns a value before my function has been completed. I know that's an issue relative to promise/observable. I need to edit my 2 functions canActivate() and isAuthorizedToAccessForms0(), but I don't know how to expect the correct behavior.
I know that pipe
, map
or then
should be added after my called to function in order to wait for the result in my guard, here are related subject 1 subject 2 subject 3
My service:
public isAuthorizedToAccessForms0(id): boolean{
var isFound = false;
//query
this.fire.collection('solutions').where(firebase.firestore.FieldPath.documentId(), '==', id).get().then(val => {
val.forEach(v => {
this.emailsAuthorizedVeolia = v.data().emailVeoliaAuthorized; //get each user
})
}).then(res => {
//Check if a user is present in the list (whenever his position is in the list)
for (let i = 0; i <= this.emailsAuthorizedVeolia.length-1 ; i++) {
if(this.authService.currentUserEmail === this.emailsAuthorizedVeolia[i]) {
isFound = true
} else {
//isFound = false by default
}
}
}).then(final => {
if (isFound === false) { //no occurrence finded
console.log("DENIED!")
isFound = false
return false
} else { //user finded
console.log("GRANTED!")
isFound = true
return true
}
});
return isFound;
}
My guard:
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
const myId = route.paramMap.get('id')
try{
const authorized = this.formAllServ.isAuthorizedToAccessForms(myId) //HERE I need to add .pipe ? .then ? .map ?
if(authorized === false) {
console.log("FALSE")
alert('You are not allowed to view this page');
this.router.navigate(['/homeUser']);
return false
}
else {
console.log("TRUE")
alert('Access granted !');
return true
}
} catch(err) {
return false;
}
}
The problem lies in the isAuthorizedToAccessForms0
method. You return the isFound
variable without waiting for end of the promise. Accordingly, the result of this function will always be false
.
You can update code of isAuthorizedToAccessForms0
like that:
public isAuthorizedToAccessForms0(id): Observable<boolean> {
return new Observable<boolean>(observer => {
this.fire.collection('solutions').where(firebase.firestore.FieldPath.documentId(), '==', id).get().then(val => {
val.forEach(v => {
this.emailsAuthorizedVeolia = v.data().emailVeoliaAuthorized; //get each user
})
}).then(res => {
//Check if user is present in the list (whenever his position in the list)
for (let i = 0; i <= this.emailsAuthorizedVeolia.length-1 ; i++) {
if(this.authService.currentUserEmail === this.emailsAuthorizedVeolia[i]) {
console.log('GRANTED!');
observer.next(true);
observer.complete();
break;
} else {
observer.next(false);
observer.complete();
console.log('DENIED!');
break;
//isFound = false by default
}
}
})
})
}
After that you should update the guard
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
const myId = route.paramMap.get('id')
try{
return this.formAllServ.isAuthorizedToAccessForms(myId).pipe(
tap((authorized) => {
if(authorized === false) {
console.log("FALSE")
alert('You are not allowed to view this page');
this.router.navigate(['/homeUser']);
}
else {
console.log("TRUE")
alert('Access granted !');
}
})
)
} catch(err) {
return of(false);
}
}
I hope this helps you