Add old blog posts from Polyglot blog

This commit is contained in:
Corbin Crutchley
2022-01-07 02:18:45 -08:00
parent 50ff1e6727
commit 8e12ef8fff
13 changed files with 414 additions and 0 deletions

View File

@@ -0,0 +1,281 @@
---
{
title: "Angular Route Guards For Authorization In A Web And Mobile Application",
description: '',
published: '2018-07-13T22:12:03.284Z',
authors: ['crutchcorn'],
tags: ['angular'],
attached: [],
license: 'cc-by-4',
originalLink: "https://www.thepolyglotdeveloper.com/2018/07/angular-route-guards-authorization-web-mobile-application/"
}
---
Youre about to release your new Angular web app. Its a photo sharing site and you want to test it, so you send a link to it to your hacker sister. Shes always messing with your stuff and she found out the URL to your admin page you added to your web app. Before you know it, shes flushed your database using a button on that admin page that you didnt restrict access to. Not a problem when using development data - but Im sure your users wouldnt be any too keen on a service where they lost all of their data. Lets fix that
## Component Checks
The most basic way to restrict a users access to any given page is to use logic that will run at the load time of the component and redirect the user if needed. Given [Angulars lifecycle hooks](https://angular.io/guide/lifecycle-hooks), we can use `ngOnInit` in order to do so.
```javascript
import {Component, OnInit} from '@angular/core';
import {Router} from '@angular/router';
import {MyAuthService} from '../../core/auth/auth.service';
@Component({
selector: 'super-secret-component',
templateUrl: '<comp-here></comp-here>',
styles: []
})
export class SecretComponent implements OnInit {
constructor(private myAuthService: MyAuthService, private router: Router) { }
ngOnInit() {
this.myAuthService.checkAuth().subscribe(isAllowed => {
if (!isAllowed) {
this.router.navigate(['/']);
}
})
}
}
```
This code sample is pretty straightforward - on loading the component, lets go ahead and check that the user is allowed to see the page, if not - lets move them to somewhere they are allowed to see. You could even add a snackbar to let them know that theyre trying to access something they shouldnt, maybe move them to a login page? Its fairly customizable.
This works perfectly fine if theres a single route youd like to restrict users from being able to see, but perhaps youd like to lockdown an entire modules routes (or just a route with child routes) or there are many different routes youd like to protect in a similar way. Of course, you could always copy and paste the code weve made before, but Angular actually provides a much easier, cleaner way of doing so.
## Introducing: Route Guards
In essence, a route guard is simply a check to tell if youre allowed to view a page or not. It can be added to any route using `canActivate` (a fairly verbose property, Id say) with a custom interface that follows [Angulars CanActivate API](https://angular.io/api/router/CanActivate). The most simplistic example of a router guard is as follows:
```javascript
// route.guard.ts
import {Injectable} from '@angular/core';
import {CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot} from '@angular/router';
import {Observable} from 'rxjs';
@Injectable()
export class RouteGuard implements CanActivate {
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
return true;
}
}
// route-routes.module.ts
import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {RouteComponent} from './posts.component';
import {RouteGuard} from '../core/route.guard';
const routes: Routes = [
{
path: '',
pathMatch: 'full',
component: RouteComponent,
canActivate: [RouteGuard]
}
];
export const routeRoutedComponents = [
RouteComponent
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class RouteRoutingModule { }
```
As you can see from the typing of `canActivate`, Angular is fairly lenient with what you need to return in order to let a user to access the page or not - it accepts a `Promise` or `Observable` of a `boolean` or even just a `boolean` itself as a return value. This guard has limited value currently, because it always returns `true` regardless of any parameters or changes. However, if we replace the `canActivate` method with something a little more useful, we can easily add back the functionality our old `ngOnInit` had:
```javascript
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
return this.myAuthService.checkAuth();
}
```
Tada! We suddenly have the same logic as before! We can now remove the `ngOnInit` from the previously added route, and keep things just as secure as before! Because we can return an `Observable`, we can even use `Observable` `pipes` like so:
```javascript
return this.myAuthService.checkAuth().pipe(tap(allowed => {
if (allowed) {
this.snackBar.open("Welcome back!");
}
}));
```
Of course, it might not be the best bet to add this logic in a guard, but its still representative of what youre capable of doing inside of a guard.
## Children Guarding
When I first learned about this, I thought it was the coolest thing in the world. I started adding it to all of my routes. Next thing I knew, I was adding it to all my routes I wanted protected in some form or another.
```javascript
[
{ path: '', pathMatch: 'full', component: RouteComponent, canActivate: [RouteGuard] },
{ path: 'list', component: RouteComponent, canActivate: [RouteGuard] },
{ path: 'detail/:id', component: RouteComponent, canActivate: [RouteGuard] }
];
```
This isnt too bad alone - but when you have hundreds of routes on a large scale project, this easily becomes unmanageable. I also had times when I wanted to add additional security to a routes children, for example a dashboard page that included some admin routes that I wanted to lock down. This is where child guards come into play.
Child guards do exactly what you think they would. They add an additional guard for children. They use a similar API as `canActivate`, and the reference to that API can be found [here](https://angular.io/api/router/CanActivateChild). So, if I were to add the following guard to my child routes:
```javascript
import {Injectable} from '@angular/core';
import {ActivatedRouteSnapshot, RouterStateSnapshot, CanActivateChild} from '@angular/router';
import {Observable} from 'rxjs';
@Injectable()
export class ChildGuard implements CanActivateChild {
canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
console.log('This child was activated!');
return true;
}
}
```
It would console log This child was activated every time you accessed a child route. How do you apply this to your routes?
```javascript
[
{ path: '', canActivateChild: [ChildGuard], children: [
{ path: '', pathMatch: 'full', component: RouteComponent, canActivate: [RouteGuard] },
{ path: 'list', component: RouteComponent, canActivate: [RouteGuard] },
{ path: 'detail/:id', component: RouteComponent, canActivate: [RouteGuard] }
]}
];
```
However, Im sure youre wondering what happens if you apply a `canActivate` alongside a `canActivateChild`. If you were to change the code so the `canActivate` runs a `console.log('This is the canActivate!')` and your routes were to look like this:
```javascript
[
{ path: '', canActivate: [ActivateGuard], canActivateChild: [ChildGuard], children: [
{ path: '', pathMatch: 'full', component: RouteComponent },
{ path: 'list', component: RouteComponent },
{ path: 'detail/:id', component: RouteComponent }
]}
];
```
And accessed the `list` route, your console would output This is the canActivate! and then This child was activated!. Of course, this has limited application when making an empty route without a component to load (thats not a child), but its massively helpful when you have a component in the parent route such as this:
```javascript
[
{
path: '',
canActivate: [AuthenticationGuard],
canActivateChild: [AuthorizationGuard],
children: [
{ path: 'admin', component: AdminComponent },
]
}
];
// NOT SHOWN: AuthenticationGuard and AuthorizationGuard. Just pretend they're code that checks what you think they would, based on the names (remember, authentication is if the user is who they say they are [AKA logged in]; authorization is making sure they have the right access [AKA if they're admin])
```
In this example, when you access the `''` path, youll make sure the user is authenticated, but doesnt care about authorization. However, when you access a child of that path (in this example, `'admin'`), it will check both authentication AND authorization.
### Route Data
But lets say that I wanted to be able to change my child route based on information that Ive stored about that particular route. For example, I typically layout my breadcrumbs by using a `data` property on my routes like such:
```javascript
[
{
path: '',
canActivateChild: [ChildGuard],
component: RouteComponent,
data: {
title: 'Main Page'
},
children: [
{ path: 'list', component: RouteComponent, data: {title: 'List Page'} },
{ path: 'detail/:id', component: RouteComponent, data: {title: 'Detail Page'} }
]
}
];
```
If I wanted to be able to add a welcome message for each page that printed their `title` on every route you accessed, you could add that logic to a `ChildGuard` logic.
```javascript
canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { // The return type has been simplified
console.log(childRoute.data.title);
return true;
}
```
Because the first argument to `canActivateChild` is an `ActivatedRouteSnapshot`, you can grab [any of the methods or properties from the API](https://angular.io/api/router/ActivatedRouteSnapshot) from the routes that are currently being called. However, something youll probably want to keep in mind is that this will occur once for every single child route being called.
## Lazy Loading
Because lazy loading using `loadChildren` is still considered a child route, all of the same rules from [Children Guarding](https://www.thepolyglotdeveloper.com/2018/07/angular-route-guards-authorization-web-mobile-application/#ChildrenGuarding) still apply. However, there are more tricks that are available for lazy loaded routes that are not otherwise available.
### Can Load
The [API for canLoad](https://angular.io/api/router/CanLoad) looks very similar to what weve seen before with `canActivate` and `canActivateChild`.
```javascript
import {Injectable} from '@angular/core';
import {Route, CanLoad} from '@angular/router';
import {Observable} from 'rxjs';
@Injectable()
export class LoadGuard implements CanLoad {
canLoad(route: Route): Observable<boolean> | Promise<boolean> | boolean {
return true;
}
}
```
Much like before, if we were to add it on a lazy loaded route:
```javascript
[
{
canLoad: [LoadGuard],
path: '',
loadChildren: './feature.module#FeatureModule'
}
]
```
It would prevent the route from loading if the return was `false`. However, the bigger difference between, say, `canActivateChild`, is how it interacts with the other methods shown here.
Lets say we have some routes shown like this:
```javascript
[
{
path: '',
canActivate: [AuthenticationGuard],
canActivateChild: [AuthorizationGuard],
children: [{
canLoad: [LoadGuard],
path: 'feature',
loadChildren: './feature.module#FeatureModule'
}, {
path: 'otherfeature',
component: RouteComponent
}]
}
];
```
In this example, if you access the `''` route, only the `AuthenticationGuard` would be called. Meanwhile, if you accessed the `'otherfeature'` route, you would load the `AuthenticationGuard`, THEN call the `AuthorizationGuard`. What order would you think the guards would load for the `'feature'` route? The answer might be a little more tricky than you expect.
The answer? `canLoad` runs first. Before `AuthenticationGuard` and before `AuthorizationGuard`. It also, unlike the other two, prevents the entire loading of the route. The advantage here is that you can stop the loading of a lazy-loaded route before doing any checks youd want to run to prevent. This would increase performance greatly in situations where youd block the loading of a page and it would be much more secure. After `canLoad` runs, then the other two run in order as they would before
## Wrap Up
Just like anything else, an Angular Router Guard is a tool. It has many uses that are really only restricted by how youre able to utilize that tool. Youre able to do service calls, logic changes, and more in order to restrict access to a page. However, its not a one-tool-fits-all solution. There will be times that a [resolver](https://angular.io/api/router/Resolve) might be able to help better, or sometimes even component logic might fit your use-case better. That being said, Guards are incredibly helpful when the time comes to use them

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@@ -0,0 +1,133 @@
---
{
title: "Change the Host File of an Android Emulator",
description: "",
published: '2019-12-27T22:12:03.284Z',
authors: ['crutchcorn'],
tags: ['android'],
attached: [],
license: 'cc-by-4',
originalLink: "https://www.thepolyglotdeveloper.com/2019/12/change-host-file-android-emulator/"
}
---
While working on a bug in one of my projects recently, I found an issue that I could only recreate on an Android device. However, due to some cross-origin resource sharing (CORS) issues on my server, I had to serve my development environment from a changed hostfile that had a specific subdomain of my project.
With the ability to use a remote Chrome debugger from your desktop to a mobile device, you can use an emulator and still have your full Chrome debugging capabilities. The only problem then, is how to get the host file to match your desktop environment. Following these steps will allow you to do just that!
## Pre-Requisites
In order to do this, youll need to install a few things first:
- [Download and install Android Studio](https://developer.android.com/studio/install)
During installation, it will ask you if you want to setup an emulator. Youll want to install all of the related Intel Virtualization packages, as it will greatly increase your speed of the emulator.
- Download and install the android-platform-tools. This will include
```
adb
```
command directly on your path for you to utilize
- For macOS, I suggest using [the Homebrew package manager](https://brew.sh/) and running `brew cask install android-platform-tools`
- For Windows, I suggest using [the Chocolatey package manager](https://chocolatey.org/) and running `choco install adb`
- For Linux, youll want to check with your package manager for one of the two above-mentioned package names
## Setup Steps
Once you have Android Studio installed, well need to setup an emulator. To do so, open the application:
![The opening screen to Android Studio](./1.png)
Then, press “Configure” in the bottom right corner. Then press the “AVD Manager” in the sub-menu.
![The sub-menu for configure in the Android Studio startup screen](./2.png)
Youll see a popup window that will show you the list of virtual devices. *These are devices that will be used in order to run an emulator*. You may already have a virtual device setup from the initial setup of Android Studio. They include the version of the operating system you use when you boot up the device. While the virtual device that was setup out-of-the-box is fine for most operations, well want to setup an older version of the emulator. This will allow us to change the host file in Android, which requires root (something the default images wont allow).
Select **Create Virtual Device**, then select a device type. In my example, I selected **Nexus 5**, but any device definition of a relatively modern phone should work.
![A popup dialog for creating a new virtual device setup](./3.png)
As mentioned before, the default images that are provided will not allow us to replace the host files. In order to do so, *we have to download an older Android image* (and one that does not include Google Play Store). To do this, I selected the **x86_64 Android 7.1.1** (non Google API version) image to download and then selected **Next**.
![The selection of the aforementioned Nougat image](./4.png)
Its worth noting that we specifically must select a non-Google version, otherwise our future commands will not work (per Googles restrictions on Google API images).
After this step, proceed to name the Android Device. *Id suggest you name it something without any spaces in order to run a command later that youll need to run*. In this case, I called the image **Nexus5**.
![My naming of the AVD](./5.png)
## Handling the Emulator
Once the AVD is initially setup, open your terminal, and find your installation path of Android Studio.
- For MacOS, this should be under **~/Library/Android/sdk**
- For Windows, this *should* be **C:\Users<username>\AppData\Local\Android\sdk**
Once in that path, you want to run a specific emulator command:
```bash
./emulator/emulator -writable-system -netdelay none -netspeed full -avd <AVDName>
```
For example, given that Im on macOS and my AVD name is **Nexus5**, I ran:
```bash
~/Library/Android/sdk/emulator/emulator -writable-system -netdelay none -netspeed full -avd Nexus5
```
This will start the emulator under specific pretenses. These pretenses will allow you to write to any file on your OS, including the host file.
![A screenshot of the above command running and the emulator started](./6.png)
Once youre done with running the emulator, open a new tab and run the following commands (in a folder you want to have the host file within):
- `adb root`
- `adb remount`
- `adb pull /system/etc/hosts`
![A screenshot of the above commands running](./7.png)
Upon running these commands, youll find a **hosts** file. *This file is the file that tells your OS what path a given domain has.* You can, for example, map `example.com` to go to a specific IP address, similar to how DNS works for most domains.
Inside the emulator, the IP address `10.0.2.2` refers to the *host* OS. For example, if youre running a local server on your Windows/MacOS/Linux machine on `localhost:3000`, you can access it using `10.0.2.2:3000` from the Android emulator.
Knowing these two things, you can change the host file to make `example.com` refer to the host by adding the following to the host file:
```plaintext
10.0.2.2 example.com
```
![A screenshot of the host file being edited using micro](./8.png)
## Pushing the Changes
Once youve made the changes to the host file that you want to have changed, youll have to push the host file to the OS of the AVD:
- `adb push ./hosts /etc/hosts`
- `adb push ./hosts /etc/system/hosts`
- `adb push ./hosts /system/etc/hosts`
While only one of these host file locations is needed to replaced, its easier to run all three than only run one to see if it worked
![A screenshot of the above commands being ran](./9.png)
In order for the changes to the host file to take effect, youll have to restart the emulator. In order to do so, youll want to press and hold the power button off to the right of the emulator. Then, press “Restart”.
If you close the emulator and re-open it using the command above, it may work, but Ive found instances where it seems to reset the host file, making you go through the whole process again
![A screenshot of the above commands being ran](./10.png)
## Seeing the Results
Finally, youre able to [sideload the Chrome APKs x86 variant](https://www.apkmirror.com/apk/google-inc/chrome) in order to load the example codebase.
So, for example, using the above hostfile as an example, we can visit `example.com:8000` when running the development mode for [the projects GitHub code](https://github.com/unicorn-utterances/unicorn-utterances/), youre able to see a preview of the [Unicorn Utterances](https://unicorn-utterances.com) website from a different domain.
![A screenshot of the above commands being ran](./11.png)
And thats it! You now know how to modify and update the host file for an emulated Android device. I hope this has helped with your development usage!