简体   繁体   中英

Angular Route Guard based on collection query result

I have a 'Teams' collection in Firestore. A team document will include a team_users map field where userID: true if they are a part of that team.

I have a query in a service that returns and displays all team documents where the logged in user id matches an ID within the team_users field.

getTeamsList(user_id: string) {
  this.teamsCollection = this.afs.collection<any>(`teams`, ref => ref.where(`team_users.${user_id}`, "==", true))
  this.teams = this.teamsCollection.snapshotChanges().pipe(
  map(actions => actions.map(a => {
    const data = a.payload.doc.data();
    const id = a.payload.doc.id;
    return { id, ...data };
  }))
 ).pipe(shareReplay());
}

What I need to achieve is a route guard that performs the following:

  1. Prevents a user accessing a team they don't belong to / use to belong to. For example, if the user has a bookmark for a document whilst they were in Team A , but have since left, they should not be able to access this route and should instead be redirected back to the url of /teams
  2. If the user does not belong to any teams (the collection query doesn't produce any results), then no routes are accessible a part from the /teams listing url

Note: A user can be a part of multiple teams. If the user was a part of Team A, B and C and has bookmarks for pages in Team A, but has since left, the route guard should prevent them from accessing any pages that were a part of Team A, but still allow access to Team B and C pages.

Does this mean that:

  1. Every URL within my app will need this guard?
  2. That every URL within my app will need to contain the team ID?

Any help would be greatly appreciated.

A user is on /teams page displaying teams (You don't need a guard on this route). When a user clicks on one of the teams navigate to /teams/teamID (Now this route needs a guard to prevent a user that doesn't belong to teamID from accessing it. To set the guard do this:

  1. Write a function that checks on firestore if a user is a member of a team.

 isTeamMember(userId: string, teamId: string): Observable < Boolean > { return this.firestore.doc < any > (`teams/${teamId}`).snapshotChanges().pipe( map(value => value.payload.data().team_users[userId] === true)); }

  1. Call the function in your Guard (CanActivate implemented like this). Remember to inject a service/store containing your current user and retrieve teamId from Url. The 2 are used to call the function on firestore

 canActivate( next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable < boolean > | Promise < boolean > | boolean { return this.firestoreService.isTeamMember(userIdFromWhereYouStoreYourCurrentUser, teamIdFromTheCurrentUrl).pipe(map(isTeamMember => { if (isTeamMember) { return true; } else { //Add your not team member logic here, eg Stay in the current route, may be show an error message return false; } }), take(1)); }

You can do it this way: 1. Make an Auth guard, let say it "AG". 2. In this "AG", make a REST call to your backend. Sending the bookmark ID, USer ID, Current Team Id. "AG" will get triggered on route change where-ever applied. 3. This REST call will compare the current team Id and the Team Id saved with that Bookmark(Assumption: Bookmark table has the column of Team Id as foreign key). 4. So when the service return TRUE/FALSE, then "AG" will know that if it is authorized or not. 5. If it is FALSE, then route user to whatever url.

Example of sample code:

@Injectable()
export class AuthGuardService implements CanActivate 
{ 
   constructor(public auth: AuthService, public router: Router) {}  

   canActivate(): boolean 
   {
    if (!this.auth.isAuthenticated()) {
      this.router.navigate(['team']);
      return false;
    }
    return true;
    }
}

Adding the route example:

export const ROUTES: Routes = [
  { path: '', component: Home},
  { 
    path: 'team/{id}',
    component: TeamComponent,
    canActivate: [AuthGuard] 
  },
  { path: '**', redirectTo: '' }
];

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM