You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by oc...@apache.org on 2021/10/22 15:01:14 UTC

[trafficcontrol] branch master updated: TPv2 Structure/Module changes (#6263)

This is an automated email from the ASF dual-hosted git repository.

ocket8888 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git


The following commit(s) were added to refs/heads/master by this push:
     new 247a5ea  TPv2 Structure/Module changes (#6263)
247a5ea is described below

commit 247a5ead5decbb17f1289b6fb92002ec37f0f063
Author: Steve Hamrick <sh...@users.noreply.github.com>
AuthorDate: Fri Oct 22 09:01:01 2021 -0600

    TPv2 Structure/Module changes (#6263)
    
    * Aapchecon work
    
    * Apachecon finish up
    
    * Remove demo stuff and clean up
    
    * Revert and comments
    
    * Lint
    
    * Core review changes
    
    * CR fixes
    
    * Fix build
    
    * Fix issues
    
    * Fix issues
    
    * Change default redirect
    
    * Code review fixes
---
 experimental/traffic-portal/.editorconfig          |   2 +-
 experimental/traffic-portal/.eslintrc.json         |   7 +-
 experimental/traffic-portal/angular.json           |   3 +
 .../traffic-portal/src/app/app-routing.module.ts   |  39 +++-----
 .../traffic-portal/src/app/app.component.html      |   4 +-
 .../traffic-portal/src/app/app.component.spec.ts   |   5 +-
 .../traffic-portal/src/app/app.component.ts        |  18 ++--
 experimental/traffic-portal/src/app/app.module.ts  | 108 +++------------------
 .../traffic-portal/src/app/app.server.module.ts    |   2 -
 .../traffic-portal/src/app/app.ui.module.ts        |  64 ++++++++++++
 .../cache-group-table.component.html               |   0
 .../cache-group-table.component.scss               |   0
 .../cache-group-table.component.spec.ts            |   7 +-
 .../cache-group-table.component.ts                 |   4 +-
 .../traffic-portal/src/app/core/core.module.ts     |  75 ++++++++++++++
 .../currentuser/currentuser.component.html         |   0
 .../currentuser/currentuser.component.scss         |   0
 .../currentuser/currentuser.component.spec.ts      |  12 ++-
 .../currentuser/currentuser.component.ts           |   6 +-
 .../update-password-dialog.component.html          |   0
 .../update-password-dialog.component.scss          |   0
 .../update-password-dialog.component.spec.ts       |  10 +-
 .../update-password-dialog.component.ts            |   8 +-
 .../dashboard/dashboard.component.html             |   2 +-
 .../dashboard/dashboard.component.scss             |   2 +-
 .../dashboard/dashboard.component.spec.ts          |  21 +++-
 .../dashboard/dashboard.component.ts               |   6 +-
 .../deliveryservice/deliveryservice.component.html |   2 +-
 .../deliveryservice/deliveryservice.component.scss |   2 +-
 .../deliveryservice.component.spec.ts              |  17 +++-
 .../deliveryservice/deliveryservice.component.ts   |   4 +-
 .../ds-card/ds-card.component.html                 |   2 +-
 .../ds-card/ds-card.component.scss                 |   2 +-
 .../ds-card/ds-card.component.spec.ts              |  12 ++-
 .../ds-card/ds-card.component.ts                   |   2 +-
 .../invalidation-jobs.component.html               |   0
 .../invalidation-jobs.component.scss               |   0
 .../invalidation-jobs.component.spec.ts            |  17 +++-
 .../invalidation-jobs.component.ts                 |   2 +-
 .../new-invalidation-job-dialog.component.html     |   0
 .../new-invalidation-job-dialog.component.scss     |   0
 .../new-invalidation-job-dialog.component.spec.ts  |   5 +-
 .../new-invalidation-job-dialog.component.ts       |   2 +-
 .../new-delivery-service.component.html            |   0
 .../new-delivery-service.component.scss            |   2 +-
 .../new-delivery-service.component.spec.ts         |  14 ++-
 .../new-delivery-service.component.ts              |   6 +-
 .../server-details/server-details.component.html   |   0
 .../server-details/server-details.component.scss   |   2 +-
 .../server-details.component.spec.ts               |  22 ++++-
 .../server-details/server-details.component.ts     |   4 +-
 .../servers-table/servers-table.component.html     |   0
 .../servers-table/servers-table.component.scss     |   0
 .../servers-table/servers-table.component.spec.ts  |  14 ++-
 .../servers-table/servers-table.component.ts       |   6 +-
 .../update-status/update-status.component.html     |   0
 .../update-status/update-status.component.scss     |   2 +-
 .../update-status/update-status.component.spec.ts  |   8 +-
 .../update-status/update-status.component.ts       |   2 +-
 .../users/users.component.html                     |   0
 .../users/users.component.scss                     |   2 +-
 .../users/users.component.spec.ts                  |  14 ++-
 .../{components => core}/users/users.component.ts  |   6 +-
 .../src/app/guards/authenticated-guard.service.ts  |  43 ++++++++
 .../src/app/guards/authentication.guard.spec.ts    |  39 ++++++++
 .../{components => }/login/login.component.html    |   2 +-
 .../{components => }/login/login.component.scss    |   2 +-
 .../{components => }/login/login.component.spec.ts |   5 +-
 .../app/{components => }/login/login.component.ts  |   8 +-
 .../src/app/models/{alert.ts => alert.model.ts}    |   0
 .../traffic-portal/src/app/models/index.ts         |   2 +-
 .../src/app/services/authentication.service.ts     | 103 --------------------
 .../traffic-portal/src/app/services/index.ts       |  16 ---
 .../alert/alert.component.html                     |   0
 .../alert/alert.component.scss                     |   0
 .../alert/alert.component.spec.ts                  |   9 +-
 .../alert/alert.component.ts                       |   2 +-
 .../{services => shared/alert}/alert.service.ts    |   4 +-
 .../api/apiservice.ts => shared/api/APIService.ts} |   0
 .../src/app/{services => shared}/api/CDNService.ts |   4 +-
 .../{services => shared}/api/CacheGroupService.ts  |   4 +-
 .../api/DeliveryServiceService.ts                  |   4 +-
 .../api/InvalidationJobService.ts                  |   4 +-
 .../api/PhysicalLocationService.ts                 |   4 +-
 .../app/{services => shared}/api/ProfileService.ts |   4 +-
 .../app/{services => shared}/api/ServerService.ts  |   4 +-
 .../app/{services => shared}/api/TypeService.ts    |   4 +-
 .../app/{services => shared}/api/UserService.ts    |   6 +-
 .../src/app/{services => shared}/api/index.ts      |   0
 .../charts}/linechart.directive.spec.ts            |   0
 .../charts}/linechart.directive.ts                 |   2 +-
 .../currentUser}/current-user.service.spec.ts      |  15 ++-
 .../currentUser}/current-user.service.ts           |  90 +++++++++++++++--
 .../generic-table/generic-table.component.html     |   0
 .../generic-table/generic-table.component.scss     |   0
 .../generic-table/generic-table.component.spec.ts  |   0
 .../generic-table/generic-table.component.ts       |   0
 .../{ => shared}/interceptor/alerts.interceptor.ts |   4 +-
 .../{ => shared}/interceptor/error.interceptor.ts  |   8 +-
 .../loading/loading.component.html                 |   0
 .../loading/loading.component.scss                 |   2 +-
 .../loading/loading.component.spec.ts              |   0
 .../loading/loading.component.ts                   |   0
 .../openable}/openable.directive.spec.ts           |   0
 .../openable}/openable.directive.ts                |   0
 .../traffic-portal/src/app/shared/shared.module.ts |  97 ++++++++++++++++++
 .../boolean-filter/boolean-filter.component.html   |   0
 .../boolean-filter/boolean-filter.component.scss   |   0
 .../boolean-filter.component.spec.ts               |   0
 .../boolean-filter/boolean-filter.component.ts     |   0
 .../ssh-cell-renderer.component.html               |   0
 .../ssh-cell-renderer.component.scss               |   0
 .../ssh-cell-renderer.component.spec.ts            |   6 +-
 .../ssh-cell-renderer.component.ts                 |   4 +-
 .../update-cell-renderer.component.html            |   0
 .../update-cell-renderer.component.scss            |   2 +-
 .../update-cell-renderer.component.spec.ts         |   0
 .../update-cell-renderer.component.ts              |   0
 .../tp-header/tp-header.component.html             |  18 ++--
 .../tp-header/tp-header.component.scss             |   2 +-
 .../tp-header/tp-header.component.spec.ts          |  10 +-
 .../tp-header/tp-header.component.ts               |   6 +-
 .../validation}/customvalidity.directive.spec.ts   |   0
 .../validation}/customvalidity.directive.ts        |   0
 124 files changed, 710 insertions(+), 407 deletions(-)

diff --git a/experimental/traffic-portal/.editorconfig b/experimental/traffic-portal/.editorconfig
index fbe52e7..947e3cf 100644
--- a/experimental/traffic-portal/.editorconfig
+++ b/experimental/traffic-portal/.editorconfig
@@ -15,7 +15,7 @@ root = true
 
 [*]
 charset = utf-8
-indent_style = tabs
+indent_style = tab
 indent_size = 4
 insert_final_newline = true
 trim_trailing_whitespace = true
diff --git a/experimental/traffic-portal/.eslintrc.json b/experimental/traffic-portal/.eslintrc.json
index 5163b31..2b5f878 100644
--- a/experimental/traffic-portal/.eslintrc.json
+++ b/experimental/traffic-portal/.eslintrc.json
@@ -155,7 +155,12 @@
 				"@typescript-eslint/no-empty-function": "error",
 				"@typescript-eslint/no-empty-interface": "error",
 				"@typescript-eslint/no-explicit-any": "error",
-				"@typescript-eslint/no-extraneous-class": "error",
+				"@typescript-eslint/no-extraneous-class": [
+					"error",
+					{
+						"allowWithDecorator": true
+					}
+				],
 				"@typescript-eslint/no-dynamic-delete": "error",
 				"@typescript-eslint/no-floating-promises": "off",
 				"@typescript-eslint/no-inferrable-types": [
diff --git a/experimental/traffic-portal/angular.json b/experimental/traffic-portal/angular.json
index ab290b7..84315fa 100644
--- a/experimental/traffic-portal/angular.json
+++ b/experimental/traffic-portal/angular.json
@@ -1,5 +1,8 @@
 {
 	"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
+	"cli": {
+		"analytics": false
+	},
 	"version": 1,
 	"newProjectRoot": "projects",
 	"projects": {
diff --git a/experimental/traffic-portal/src/app/app-routing.module.ts b/experimental/traffic-portal/src/app/app-routing.module.ts
index a95bfca..8abeebd 100644
--- a/experimental/traffic-portal/src/app/app-routing.module.ts
+++ b/experimental/traffic-portal/src/app/app-routing.module.ts
@@ -11,31 +11,24 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
-import { NgModule } from "@angular/core";
-import { Routes, RouterModule } from "@angular/router";
-import { CacheGroupTableComponent } from "./components/cache-groups/cache-group-table/cache-group-table.component";
+import {NgModule} from "@angular/core";
+import {RouterModule, Routes} from "@angular/router";
 
-import { CurrentuserComponent } from "./components/currentuser/currentuser.component";
-import { DashboardComponent } from "./components/dashboard/dashboard.component";
-import { DeliveryserviceComponent } from "./components/deliveryservice/deliveryservice.component";
-import { InvalidationJobsComponent } from "./components/invalidation-jobs/invalidation-jobs.component";
-import { LoginComponent } from "./components/login/login.component";
-import { NewDeliveryServiceComponent } from "./components/new-delivery-service/new-delivery-service.component";
-import { ServerDetailsComponent } from "./components/servers/server-details/server-details.component";
-import { ServersTableComponent } from "./components/servers/servers-table/servers-table.component";
-import { UsersComponent } from "./components/users/users.component";
+import {LoginComponent} from "./login/login.component";
+import {AuthenticatedGuard} from "./guards/authenticated-guard.service";
 
 const routes: Routes = [
-	{ component: DashboardComponent, path: "" },
-	{ component: LoginComponent, path: "login" },
-	{ component: UsersComponent, path: "users"},
-	{ component: CurrentuserComponent, path: "me"},
-	{ component: NewDeliveryServiceComponent, path: "new.Delivery.Service"},
-	{ component: DeliveryserviceComponent, path: "deliveryservice/:id"},
-	{ component: InvalidationJobsComponent, path: "deliveryservice/:id/invalidation-jobs"},
-	{ component: ServersTableComponent, path: "servers"},
-	{ component: CacheGroupTableComponent, path: "cache-groups"},
-	{ component: ServerDetailsComponent, path: "server/:id"}
+	{component: LoginComponent, path: "login"},
+	{
+		canLoad: [AuthenticatedGuard],
+		children: [{
+			loadChildren: async (): Promise<object> => import("./core/core.module")
+				.then(mod => mod.CoreModule),
+			path: ""
+		}],
+		path: "core",
+	},
+	{path: "", pathMatch: "full", redirectTo: "login"}
 ];
 
 /**
@@ -48,6 +41,4 @@ const routes: Routes = [
 		relativeLinkResolution: "legacy"
 	})],
 })
-// This is a necessary empty class. All of its data/logic come from the decorator.
-// eslint-disable-next-line @typescript-eslint/no-extraneous-class
 export class AppRoutingModule { }
diff --git a/experimental/traffic-portal/src/app/app.component.html b/experimental/traffic-portal/src/app/app.component.html
index e94df79..ed69313 100644
--- a/experimental/traffic-portal/src/app/app.component.html
+++ b/experimental/traffic-portal/src/app/app.component.html
@@ -11,5 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 -->
-<router-outlet></router-outlet>
+<div>
+	<router-outlet style="height:100%"></router-outlet>
+</div>
 <tp-alert></tp-alert>
diff --git a/experimental/traffic-portal/src/app/app.component.spec.ts b/experimental/traffic-portal/src/app/app.component.spec.ts
index ddb4bdc..e462fd4 100644
--- a/experimental/traffic-portal/src/app/app.component.spec.ts
+++ b/experimental/traffic-portal/src/app/app.component.spec.ts
@@ -17,14 +17,14 @@ import { TestBed, waitForAsync } from "@angular/core/testing";
 import { RouterTestingModule } from "@angular/router/testing";
 import { MatSnackBarModule } from "@angular/material/snack-bar";
 
+import { CurrentUserService } from "src/app/shared/currentUser/current-user.service";
 import { AppComponent } from "./app.component";
-import { AlertComponent } from "./components/alert/alert.component";
 
 describe("AppComponent", () => {
 	beforeEach(waitForAsync(() => {
+		const mockCurrentUserService = jasmine.createSpyObj(["updateCurrentUser", "login", "logout"]);
 		TestBed.configureTestingModule({
 			declarations: [
-				AlertComponent,
 				AppComponent
 			],
 			imports: [
@@ -32,6 +32,7 @@ describe("AppComponent", () => {
 				RouterTestingModule,
 				MatSnackBarModule
 			],
+			providers: [ { provide: CurrentUserService, useValue: mockCurrentUserService }]
 		}).compileComponents();
 	}));
 
diff --git a/experimental/traffic-portal/src/app/app.component.ts b/experimental/traffic-portal/src/app/app.component.ts
index 680b718..efc350e 100644
--- a/experimental/traffic-portal/src/app/app.component.ts
+++ b/experimental/traffic-portal/src/app/app.component.ts
@@ -13,10 +13,10 @@
 */
 
 import { Component, OnInit } from "@angular/core";
-import { Router } from "@angular/router";
+import {Router} from "@angular/router";
 
-import { User } from "./models";
-import { AuthenticationService } from "./services";
+import { User } from "src/app/models";
+import {CurrentUserService} from "src/app/shared/currentUser/current-user.service";
 
 /**
  * The most basic component that contains everything else. This should be kept pretty simple.
@@ -36,7 +36,7 @@ export class AppComponent implements OnInit {
 	/**
 	 * Constructor.
 	 */
-	constructor(private readonly router: Router, private readonly auth: AuthenticationService) {
+	constructor(private readonly router: Router, private readonly auth: CurrentUserService) {
 	}
 
 	/**
@@ -51,13 +51,9 @@ export class AppComponent implements OnInit {
 	 * Sets up the current user.
 	 */
 	public ngOnInit(): void {
-		this.auth.updateCurrentUser().then(
-			success =>  {
-				if (success) {
-					this.currentUser = this.auth.currentUser;
-				}
-			}
-		);
+		this.auth.userChanged.subscribe(user => {
+			this.currentUser = user;
+		});
 	}
 
 }
diff --git a/experimental/traffic-portal/src/app/app.module.ts b/experimental/traffic-portal/src/app/app.module.ts
index 51717c8..c88773e 100644
--- a/experimental/traffic-portal/src/app/app.module.ts
+++ b/experimental/traffic-portal/src/app/app.module.ts
@@ -18,60 +18,19 @@
  * `ng generate` to create new things (and then fix formatting/missing license)
  */
 
-import { HttpClientModule, HTTP_INTERCEPTORS } from "@angular/common/http";
+import { HttpClientModule } from "@angular/common/http";
 import { NgModule } from "@angular/core";
-import { ReactiveFormsModule, FormsModule } from "@angular/forms";
 import { BrowserModule } from "@angular/platform-browser";
-import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
-import { MatButtonModule } from "@angular/material/button";
-import { MatCardModule } from "@angular/material/card";
-import { MatNativeDateModule } from "@angular/material/core";
-import { MatDividerModule } from "@angular/material/divider";
-import { MatExpansionModule } from "@angular/material/expansion";
-import { MatInputModule } from "@angular/material/input";
-import { MatListModule } from "@angular/material/list";
-import { MatRadioModule } from "@angular/material/radio";
-import { MatSnackBarModule } from "@angular/material/snack-bar";
-import { MatStepperModule } from "@angular/material/stepper";
-import { MatToolbarModule } from "@angular/material/toolbar";
-import { MatDialogModule } from "@angular/material/dialog";
-import { MatDatepickerModule } from "@angular/material/datepicker";
-
-import { FontAwesomeModule } from "@fortawesome/angular-fontawesome";
-import { AgGridModule } from "ag-grid-angular";
 import * as Chart from "chart.js";
 
 // Routing, Components, Directives and Interceptors
+import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
 import { AppRoutingModule } from "./app-routing.module";
 import { AppComponent } from "./app.component";
-import { AlertComponent } from "./components/alert/alert.component";
-import { CurrentuserComponent } from "./components/currentuser/currentuser.component";
-import { DashboardComponent } from "./components/dashboard/dashboard.component";
-import { DeliveryserviceComponent } from "./components/deliveryservice/deliveryservice.component";
-import { DsCardComponent } from "./components/ds-card/ds-card.component";
-import { InvalidationJobsComponent } from "./components/invalidation-jobs/invalidation-jobs.component";
-import { LoadingComponent } from "./components/loading/loading.component";
-import { LoginComponent } from "./components/login/login.component";
-import { NewDeliveryServiceComponent } from "./components/new-delivery-service/new-delivery-service.component";
-import { ServersTableComponent } from "./components/servers/servers-table/servers-table.component";
-import { SSHCellRendererComponent } from "./components/table-components/ssh-cell-renderer/ssh-cell-renderer.component";
-import { TpHeaderComponent } from "./components/tp-header/tp-header.component";
-import { UsersComponent } from "./components/users/users.component";
-import { CustomvalidityDirective } from "./directives/customvalidity.directive";
-import { LinechartDirective } from "./directives/linechart.directive";
-import { OpenableDirective } from "./directives/openable.directive";
-import { AlertInterceptor } from "./interceptor/alerts.interceptor";
-import { ErrorInterceptor } from "./interceptor/error.interceptor";
-import { GenericTableComponent } from "./components/generic-table/generic-table.component";
-import { CacheGroupTableComponent } from "./components/cache-groups/cache-group-table/cache-group-table.component";
-import { BooleanFilterComponent } from "./components/table-components/boolean-filter/boolean-filter.component";
-import { ServerDetailsComponent } from "./components/servers/server-details/server-details.component";
-import { UpdateCellRendererComponent } from "./components/table-components/update-cell-renderer/update-cell-renderer.component";
-import { UpdateStatusComponent } from "./components/servers/update-status/update-status.component";
-import {
-	NewInvalidationJobDialogComponent
-} from "./components/invalidation-jobs/new-invalidation-job-dialog/new-invalidation-job-dialog.component";
-import { UpdatePasswordDialogComponent } from "./components/currentuser/update-password-dialog/update-password-dialog.component";
+import { LoginComponent } from "./login/login.component";
+import {AppUIModule} from "./app.ui.module";
+import {SharedModule} from "./shared/shared.module";
+import {AuthenticatedGuard} from "./guards/authenticated-guard.service";
 
 // TODO: Figure out the actual typing here.
 Chart.plugins.register({
@@ -100,61 +59,18 @@ Chart.plugins.register({
 	bootstrap: [AppComponent],
 	declarations: [
 		AppComponent,
-		LoginComponent,
-		DashboardComponent,
-		DsCardComponent,
-		AlertComponent,
-		UsersComponent,
-		NewDeliveryServiceComponent,
-		TpHeaderComponent,
-		LoadingComponent,
-		DeliveryserviceComponent,
-		LinechartDirective,
-		InvalidationJobsComponent,
-		OpenableDirective,
-		CustomvalidityDirective,
-		CurrentuserComponent,
-		ServersTableComponent,
-		GenericTableComponent,
-		CacheGroupTableComponent,
-		BooleanFilterComponent,
-		ServerDetailsComponent,
-		UpdateCellRendererComponent,
-		UpdateStatusComponent,
-		NewInvalidationJobDialogComponent,
-		UpdatePasswordDialogComponent
-	],
-	entryComponents: [
-		SSHCellRendererComponent
+		LoginComponent
 	],
 	imports: [
 		BrowserModule.withServerTransition({ appId: "serverApp" }),
+		BrowserAnimationsModule,
 		AppRoutingModule,
 		HttpClientModule,
-		ReactiveFormsModule,
-		FormsModule,
-		FontAwesomeModule,
-		AgGridModule.withComponents([]),
-		BrowserAnimationsModule,
-		MatButtonModule,
-		MatCardModule,
-		MatDividerModule,
-		MatExpansionModule,
-		MatInputModule,
-		MatListModule,
-		MatRadioModule,
-		MatSnackBarModule,
-		MatStepperModule,
-		MatToolbarModule,
-		MatDialogModule,
-		MatDatepickerModule,
-		MatNativeDateModule,
+		AppUIModule,
+		SharedModule
 	],
 	providers: [
-		{multi: true, provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor},
-		{multi: true, provide: HTTP_INTERCEPTORS, useClass: AlertInterceptor}
-	],
+		AuthenticatedGuard
+	]
 })
-// This is a necessary empty class. All of its data/logic come from the decorator.
-// eslint-disable-next-line @typescript-eslint/no-extraneous-class
 export class AppModule { }
diff --git a/experimental/traffic-portal/src/app/app.server.module.ts b/experimental/traffic-portal/src/app/app.server.module.ts
index 0c612d3..8ccdcfb 100644
--- a/experimental/traffic-portal/src/app/app.server.module.ts
+++ b/experimental/traffic-portal/src/app/app.server.module.ts
@@ -29,6 +29,4 @@ import { AppModule } from "./app.module";
 		ServerModule,
 	],
 })
-// This empty class needs to exist; the decorator provides all of its data/logic
-// eslint-disable-next-line @typescript-eslint/no-extraneous-class
 export class AppServerModule {}
diff --git a/experimental/traffic-portal/src/app/app.ui.module.ts b/experimental/traffic-portal/src/app/app.ui.module.ts
new file mode 100644
index 0000000..35db382
--- /dev/null
+++ b/experimental/traffic-portal/src/app/app.ui.module.ts
@@ -0,0 +1,64 @@
+/*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import { NgModule } from "@angular/core";
+import { AgGridModule } from "ag-grid-angular";
+import {FormsModule, ReactiveFormsModule} from "@angular/forms";
+import {FontAwesomeModule} from "@fortawesome/angular-fontawesome";
+import {MatButtonModule} from "@angular/material/button";
+import {MatCardModule} from "@angular/material/card";
+import {MatDividerModule} from "@angular/material/divider";
+import {MatInputModule} from "@angular/material/input";
+import {MatListModule} from "@angular/material/list";
+import {MatRadioModule} from "@angular/material/radio";
+import {MatSnackBarModule} from "@angular/material/snack-bar";
+import {MatStepperModule} from "@angular/material/stepper";
+import {MatNativeDateModule} from "@angular/material/core";
+import {MatDialogModule} from "@angular/material/dialog";
+import {MatDatepickerModule} from "@angular/material/datepicker";
+import {MatToolbarModule} from "@angular/material/toolbar";
+import {MatExpansionModule} from "@angular/material/expansion";
+import {MatButtonToggleModule} from "@angular/material/button-toggle";
+
+/**
+ * AppUIModule is the Angular Module that contains the ui dependencies of
+ * the app.
+ */
+@NgModule({
+	bootstrap: [],
+	exports: [
+		AgGridModule,
+
+		ReactiveFormsModule,
+		FormsModule,
+
+		FontAwesomeModule,
+
+		MatButtonModule,
+		MatCardModule,
+		MatDividerModule,
+		MatExpansionModule,
+		MatInputModule,
+		MatListModule,
+		MatRadioModule,
+		MatSnackBarModule,
+		MatStepperModule,
+		MatToolbarModule,
+		MatDialogModule,
+		MatDatepickerModule,
+		MatNativeDateModule,
+		MatButtonToggleModule
+	]
+})
+export class AppUIModule {}
diff --git a/experimental/traffic-portal/src/app/components/cache-groups/cache-group-table/cache-group-table.component.html b/experimental/traffic-portal/src/app/core/cache-groups/cache-group-table/cache-group-table.component.html
similarity index 100%
rename from experimental/traffic-portal/src/app/components/cache-groups/cache-group-table/cache-group-table.component.html
rename to experimental/traffic-portal/src/app/core/cache-groups/cache-group-table/cache-group-table.component.html
diff --git a/experimental/traffic-portal/src/app/components/cache-groups/cache-group-table/cache-group-table.component.scss b/experimental/traffic-portal/src/app/core/cache-groups/cache-group-table/cache-group-table.component.scss
similarity index 100%
rename from experimental/traffic-portal/src/app/components/cache-groups/cache-group-table/cache-group-table.component.scss
rename to experimental/traffic-portal/src/app/core/cache-groups/cache-group-table/cache-group-table.component.scss
diff --git a/experimental/traffic-portal/src/app/components/cache-groups/cache-group-table/cache-group-table.component.spec.ts b/experimental/traffic-portal/src/app/core/cache-groups/cache-group-table/cache-group-table.component.spec.ts
similarity index 87%
rename from experimental/traffic-portal/src/app/components/cache-groups/cache-group-table/cache-group-table.component.spec.ts
rename to experimental/traffic-portal/src/app/core/cache-groups/cache-group-table/cache-group-table.component.spec.ts
index c873732..6edfdbd 100644
--- a/experimental/traffic-portal/src/app/components/cache-groups/cache-group-table/cache-group-table.component.spec.ts
+++ b/experimental/traffic-portal/src/app/core/cache-groups/cache-group-table/cache-group-table.component.spec.ts
@@ -16,6 +16,7 @@ import { ComponentFixture, TestBed } from "@angular/core/testing";
 import { ReactiveFormsModule } from "@angular/forms";
 import { RouterTestingModule } from "@angular/router/testing";
 
+import {CacheGroupService} from "../../../shared/api";
 import { CacheGroupTableComponent } from "./cache-group-table.component";
 
 describe("CacheGroupTableComponent", () => {
@@ -23,9 +24,13 @@ describe("CacheGroupTableComponent", () => {
 	let fixture: ComponentFixture<CacheGroupTableComponent>;
 
 	beforeEach(async () => {
+		const mockAPIService = jasmine.createSpyObj(["getCacheGroups"]);
 		await TestBed.configureTestingModule({
 			declarations: [ CacheGroupTableComponent ],
-			imports: [ReactiveFormsModule, HttpClientModule, RouterTestingModule]
+			imports: [ReactiveFormsModule, HttpClientModule, RouterTestingModule],
+			providers: [
+				{ provide: CacheGroupService, useValue: mockAPIService }
+			]
 		})
 			.compileComponents();
 	});
diff --git a/experimental/traffic-portal/src/app/components/cache-groups/cache-group-table/cache-group-table.component.ts b/experimental/traffic-portal/src/app/core/cache-groups/cache-group-table/cache-group-table.component.ts
similarity index 96%
rename from experimental/traffic-portal/src/app/components/cache-groups/cache-group-table/cache-group-table.component.ts
rename to experimental/traffic-portal/src/app/core/cache-groups/cache-group-table/cache-group-table.component.ts
index d45528a..89aa414 100644
--- a/experimental/traffic-portal/src/app/components/cache-groups/cache-group-table/cache-group-table.component.ts
+++ b/experimental/traffic-portal/src/app/core/cache-groups/cache-group-table/cache-group-table.component.ts
@@ -18,8 +18,8 @@ import { ActivatedRoute } from "@angular/router";
 import { BehaviorSubject } from "rxjs";
 
 import { CacheGroup } from "src/app/models/cache-groups";
-import { CacheGroupService } from "src/app/services/api";
-import { ContextMenuActionEvent, ContextMenuItem } from "../../generic-table/generic-table.component";
+import { CacheGroupService } from "src/app/shared/api";
+import { ContextMenuActionEvent, ContextMenuItem } from "../../../shared/generic-table/generic-table.component";
 
 /**
  * CacheGroupTableComponent is the controller for the "Cache Groups" table.
diff --git a/experimental/traffic-portal/src/app/core/core.module.ts b/experimental/traffic-portal/src/app/core/core.module.ts
new file mode 100644
index 0000000..9f74e96
--- /dev/null
+++ b/experimental/traffic-portal/src/app/core/core.module.ts
@@ -0,0 +1,75 @@
+/*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+import { NgModule } from "@angular/core";
+import {RouterModule, Routes} from "@angular/router";
+import {CommonModule} from "@angular/common";
+import {AppUIModule} from "../app.ui.module";
+import {SharedModule} from "../shared/shared.module";
+import {AuthenticatedGuard} from "../guards/authenticated-guard.service";
+import {InvalidationJobsComponent} from "./invalidation-jobs/invalidation-jobs.component";
+import {UsersComponent} from "./users/users.component";
+import {ServerDetailsComponent} from "./servers/server-details/server-details.component";
+import {ServersTableComponent} from "./servers/servers-table/servers-table.component";
+import {UpdateStatusComponent} from "./servers/update-status/update-status.component";
+import {DeliveryserviceComponent} from "./deliveryservice/deliveryservice.component";
+import {NewDeliveryServiceComponent} from "./new-delivery-service/new-delivery-service.component";
+import {DashboardComponent} from "./dashboard/dashboard.component";
+import {CacheGroupTableComponent} from "./cache-groups/cache-group-table/cache-group-table.component";
+import {CurrentuserComponent} from "./currentuser/currentuser.component";
+import {UpdatePasswordDialogComponent} from "./currentuser/update-password-dialog/update-password-dialog.component";
+import {DsCardComponent} from "./ds-card/ds-card.component";
+import {NewInvalidationJobDialogComponent} from "./invalidation-jobs/new-invalidation-job-dialog/new-invalidation-job-dialog.component";
+
+
+const routes: Routes = [
+	{ canActivate: [AuthenticatedGuard], component: DashboardComponent, path: "" },
+	{ canActivate: [AuthenticatedGuard], component: UsersComponent, path: "users" },
+	{ canActivate: [AuthenticatedGuard], component: ServersTableComponent, path: "servers" },
+	{ canActivate: [AuthenticatedGuard], component: ServerDetailsComponent, path: "server/:id" },
+	{ canActivate: [AuthenticatedGuard], component: DeliveryserviceComponent, path: "deliveryservice/:id" },
+	{ canActivate: [AuthenticatedGuard], component: InvalidationJobsComponent, path: "deliveryservice/:id/invalidation-jobs" },
+	{ canActivate: [AuthenticatedGuard], component: CurrentuserComponent, path: "me" },
+	{ canActivate: [AuthenticatedGuard], component: NewDeliveryServiceComponent, path: "new.Delivery.Service" },
+	{ canActivate: [AuthenticatedGuard], component: CacheGroupTableComponent, path: "cache-groups" }
+];
+
+/**
+ * CoreModule contains code that only logged in users will be served.
+ */
+@NgModule({
+	declarations: [
+		UsersComponent,
+		ServerDetailsComponent,
+		ServersTableComponent,
+		DeliveryserviceComponent,
+		NewDeliveryServiceComponent,
+		CurrentuserComponent,
+		UpdatePasswordDialogComponent,
+		DashboardComponent,
+		DsCardComponent,
+		InvalidationJobsComponent,
+		CacheGroupTableComponent,
+		NewInvalidationJobDialogComponent,
+		UpdateStatusComponent
+	],
+	exports: [
+	],
+	imports: [
+		SharedModule,
+		AppUIModule,
+		CommonModule,
+		RouterModule.forChild(routes)
+	]
+})
+export class CoreModule { }
diff --git a/experimental/traffic-portal/src/app/components/currentuser/currentuser.component.html b/experimental/traffic-portal/src/app/core/currentuser/currentuser.component.html
similarity index 100%
rename from experimental/traffic-portal/src/app/components/currentuser/currentuser.component.html
rename to experimental/traffic-portal/src/app/core/currentuser/currentuser.component.html
diff --git a/experimental/traffic-portal/src/app/components/currentuser/currentuser.component.scss b/experimental/traffic-portal/src/app/core/currentuser/currentuser.component.scss
similarity index 100%
rename from experimental/traffic-portal/src/app/components/currentuser/currentuser.component.scss
rename to experimental/traffic-portal/src/app/core/currentuser/currentuser.component.scss
diff --git a/experimental/traffic-portal/src/app/components/currentuser/currentuser.component.spec.ts b/experimental/traffic-portal/src/app/core/currentuser/currentuser.component.spec.ts
similarity index 79%
rename from experimental/traffic-portal/src/app/components/currentuser/currentuser.component.spec.ts
rename to experimental/traffic-portal/src/app/core/currentuser/currentuser.component.spec.ts
index b15e8bb..bc92174 100644
--- a/experimental/traffic-portal/src/app/components/currentuser/currentuser.component.spec.ts
+++ b/experimental/traffic-portal/src/app/core/currentuser/currentuser.component.spec.ts
@@ -16,10 +16,11 @@ import { waitForAsync, ComponentFixture, TestBed } from "@angular/core/testing";
 import { MatDialogModule } from "@angular/material/dialog";
 import { RouterTestingModule } from "@angular/router/testing";
 
-import { UserService } from "src/app/services/api";
+import { UserService } from "src/app/shared/api";
 
+import { CurrentUserService } from "src/app/shared/currentUser/current-user.service";
 import { User } from "../../models";
-import { TpHeaderComponent } from "../tp-header/tp-header.component";
+import {TpHeaderComponent} from "../../shared/tp-header/tp-header.component";
 import { CurrentuserComponent } from "./currentuser.component";
 
 describe("CurrentuserComponent", () => {
@@ -28,6 +29,8 @@ describe("CurrentuserComponent", () => {
 
 	beforeEach(waitForAsync(() => {
 		const mockAPIService = jasmine.createSpyObj(["getRoles", "getCurrentUser"]);
+		const mockCurrentUserService = jasmine.createSpyObj(["getCurrentUser", "getCapabilities",
+			"getLoggedIn", "setUser", "hasPermission", "logout", "updateCurrentUser", "login", "logout"]);
 		mockAPIService.getRoles.and.returnValue(new Promise(resolve => resolve([])));
 		mockAPIService.getCurrentUser.and.returnValue(new Promise(resolve => resolve({
 			id: 0,
@@ -44,9 +47,12 @@ describe("CurrentuserComponent", () => {
 				HttpClientModule,
 				RouterTestingModule,
 				MatDialogModule
+			],
+			providers: [
+				{ provide: UserService, useValue: mockAPIService},
+				{ provide: CurrentUserService, useValue: mockCurrentUserService},
 			]
 		});
-		TestBed.overrideProvider(UserService, { useValue: mockAPIService });
 		TestBed.compileComponents();
 	}));
 
diff --git a/experimental/traffic-portal/src/app/components/currentuser/currentuser.component.ts b/experimental/traffic-portal/src/app/core/currentuser/currentuser.component.ts
similarity index 96%
rename from experimental/traffic-portal/src/app/components/currentuser/currentuser.component.ts
rename to experimental/traffic-portal/src/app/core/currentuser/currentuser.component.ts
index 1c62341..8d276b0 100644
--- a/experimental/traffic-portal/src/app/components/currentuser/currentuser.component.ts
+++ b/experimental/traffic-portal/src/app/core/currentuser/currentuser.component.ts
@@ -15,10 +15,10 @@ import { Component, OnInit } from "@angular/core";
 import { MatDialog } from "@angular/material/dialog";
 import { ActivatedRoute, Router } from "@angular/router";
 import { faEdit } from "@fortawesome/free-solid-svg-icons";
-import { UserService } from "src/app/services/api";
+import { UserService } from "src/app/shared/api";
 
 import { User } from "src/app/models";
-import { AuthenticationService } from "src/app/services";
+import {CurrentUserService} from "src/app/shared/currentUser/current-user.service";
 import { UpdatePasswordDialogComponent } from "./update-password-dialog/update-password-dialog.component";
 
 /**
@@ -48,7 +48,7 @@ export class CurrentuserComponent implements OnInit {
 	public editUser: User | null = null;
 
 	constructor(
-		private readonly auth: AuthenticationService,
+		private readonly auth: CurrentUserService,
 		private readonly api: UserService,
 		private readonly dialog: MatDialog,
 		private readonly route: ActivatedRoute,
diff --git a/experimental/traffic-portal/src/app/components/currentuser/update-password-dialog/update-password-dialog.component.html b/experimental/traffic-portal/src/app/core/currentuser/update-password-dialog/update-password-dialog.component.html
similarity index 100%
rename from experimental/traffic-portal/src/app/components/currentuser/update-password-dialog/update-password-dialog.component.html
rename to experimental/traffic-portal/src/app/core/currentuser/update-password-dialog/update-password-dialog.component.html
diff --git a/experimental/traffic-portal/src/app/components/currentuser/update-password-dialog/update-password-dialog.component.scss b/experimental/traffic-portal/src/app/core/currentuser/update-password-dialog/update-password-dialog.component.scss
similarity index 100%
rename from experimental/traffic-portal/src/app/components/currentuser/update-password-dialog/update-password-dialog.component.scss
rename to experimental/traffic-portal/src/app/core/currentuser/update-password-dialog/update-password-dialog.component.scss
diff --git a/experimental/traffic-portal/src/app/components/currentuser/update-password-dialog/update-password-dialog.component.spec.ts b/experimental/traffic-portal/src/app/core/currentuser/update-password-dialog/update-password-dialog.component.spec.ts
similarity index 87%
rename from experimental/traffic-portal/src/app/components/currentuser/update-password-dialog/update-password-dialog.component.spec.ts
rename to experimental/traffic-portal/src/app/core/currentuser/update-password-dialog/update-password-dialog.component.spec.ts
index 53cd71e..7c31dfc 100644
--- a/experimental/traffic-portal/src/app/components/currentuser/update-password-dialog/update-password-dialog.component.spec.ts
+++ b/experimental/traffic-portal/src/app/core/currentuser/update-password-dialog/update-password-dialog.component.spec.ts
@@ -16,8 +16,8 @@ import { ComponentFixture, TestBed } from "@angular/core/testing";
 import { MatDialogModule, MatDialogRef } from "@angular/material/dialog";
 import { RouterTestingModule } from "@angular/router/testing";
 import { User } from "src/app/models";
-import { AuthenticationService } from "src/app/services";
-import { UserService } from "src/app/services/api";
+import { CurrentUserService } from "src/app/shared/currentUser/current-user.service";
+import { UserService } from "src/app/shared/api";
 
 import { UpdatePasswordDialogComponent } from "./update-password-dialog.component";
 
@@ -27,9 +27,10 @@ describe("UpdatePasswordDialogComponent", () => {
 	let dialogOpen = true;
 	let updated = false;
 
-	const mockAPIService = jasmine.createSpyObj(["updateCurrentUser", "getCurrentUser"]);
+	const mockAPIService = jasmine.createSpyObj(["updateCurrentUser", "getCurrentUser", "saveCurrentUser"], );
 	mockAPIService.updateCurrentUser.and.returnValue(new Promise(resolve => resolve(true)));
 	mockAPIService.getCurrentUser.and.returnValue(new Promise<User>(resolve => resolve({id: -1, newUser: false, username: ""})));
+	mockAPIService.currentUser = {id: 1, newUser: false, username: "hello"};
 
 	beforeEach(async () => {
 		dialogOpen = true;
@@ -43,7 +44,7 @@ describe("UpdatePasswordDialogComponent", () => {
 					updated = upd ?? false;
 				}}},
 				{provide: UserService, useValue: mockAPIService},
-				{provide: AuthenticationService, useValue: {currentUser: {id: -1, newUser: false, username: ""}}}
+				{provide: CurrentUserService, useValue: mockAPIService}
 			]
 		}).compileComponents();
 	});
@@ -66,6 +67,7 @@ describe("UpdatePasswordDialogComponent", () => {
 			}
 		);
 		component.confirm = component.password;
+		mockAPIService.saveCurrentUser.and.returnValue(new Promise(r => r(true)));
 		await component.submit(new Event("submit"));
 		expect(dialogOpen).toBeFalse();
 		expect(updated).toBeTrue();
diff --git a/experimental/traffic-portal/src/app/components/currentuser/update-password-dialog/update-password-dialog.component.ts b/experimental/traffic-portal/src/app/core/currentuser/update-password-dialog/update-password-dialog.component.ts
similarity index 89%
rename from experimental/traffic-portal/src/app/components/currentuser/update-password-dialog/update-password-dialog.component.ts
rename to experimental/traffic-portal/src/app/core/currentuser/update-password-dialog/update-password-dialog.component.ts
index 5b2685d..048aea4 100644
--- a/experimental/traffic-portal/src/app/components/currentuser/update-password-dialog/update-password-dialog.component.ts
+++ b/experimental/traffic-portal/src/app/core/currentuser/update-password-dialog/update-password-dialog.component.ts
@@ -15,8 +15,7 @@ import { Component } from "@angular/core";
 import { MatDialogRef } from "@angular/material/dialog";
 import { Subject } from "rxjs";
 
-import { AuthenticationService } from "src/app/services";
-import { UserService } from "src/app/services/api";
+import {CurrentUserService} from "src/app/shared/currentUser/current-user.service";
 
 /**
  * This is the controller for the "Update Password" dialog box/form.
@@ -37,8 +36,7 @@ export class UpdatePasswordDialogComponent {
 
 	constructor(
 		private readonly dialog: MatDialogRef<UpdatePasswordDialogComponent>,
-		private readonly auth: AuthenticationService,
-		private readonly api: UserService
+		private readonly auth: CurrentUserService
 	) { }
 
 	/**
@@ -72,7 +70,7 @@ export class UpdatePasswordDialogComponent {
 
 		user.localPasswd = this.password;
 		user.confirmLocalPasswd = this.confirm;
-		return this.api.updateCurrentUser(user).then(
+		return this.auth.saveCurrentUser(user).then(
 			success => {
 				if (success) {
 					this.dialog.close(true);
diff --git a/experimental/traffic-portal/src/app/components/dashboard/dashboard.component.html b/experimental/traffic-portal/src/app/core/dashboard/dashboard.component.html
similarity index 94%
rename from experimental/traffic-portal/src/app/components/dashboard/dashboard.component.html
rename to experimental/traffic-portal/src/app/core/dashboard/dashboard.component.html
index 2eb504e..4d1c440 100644
--- a/experimental/traffic-portal/src/app/components/dashboard/dashboard.component.html
+++ b/experimental/traffic-portal/src/app/core/dashboard/dashboard.component.html
@@ -19,4 +19,4 @@ limitations under the License.
 	</article>
 	<div id="loading" *ngIf="loading"><tp-loading></tp-loading></div>
 </main>
-<a mat-fab id="new" *ngIf="canCreateDeliveryServices" title="Create a new Delivery Service" routerLink="/new.Delivery.Service">+</a>
+<a mat-fab id="new" *ngIf="canCreateDeliveryServices" title="Create a new Delivery Service" routerLink="/core/new.Delivery.Service">+</a>
diff --git a/experimental/traffic-portal/src/app/components/dashboard/dashboard.component.scss b/experimental/traffic-portal/src/app/core/dashboard/dashboard.component.scss
similarity index 98%
rename from experimental/traffic-portal/src/app/components/dashboard/dashboard.component.scss
rename to experimental/traffic-portal/src/app/core/dashboard/dashboard.component.scss
index 178eb1c..7f2d0f8 100644
--- a/experimental/traffic-portal/src/app/components/dashboard/dashboard.component.scss
+++ b/experimental/traffic-portal/src/app/core/dashboard/dashboard.component.scss
@@ -11,7 +11,7 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
-@import '../../../theme.scss';
+@import 'src/theme';
 
 #deliveryservices {
 	width: 80%;
diff --git a/experimental/traffic-portal/src/app/components/dashboard/dashboard.component.spec.ts b/experimental/traffic-portal/src/app/core/dashboard/dashboard.component.spec.ts
similarity index 70%
rename from experimental/traffic-portal/src/app/components/dashboard/dashboard.component.spec.ts
rename to experimental/traffic-portal/src/app/core/dashboard/dashboard.component.spec.ts
index 96d67dd..3af217c 100644
--- a/experimental/traffic-portal/src/app/components/dashboard/dashboard.component.spec.ts
+++ b/experimental/traffic-portal/src/app/core/dashboard/dashboard.component.spec.ts
@@ -15,14 +15,16 @@ import { HttpClientModule } from "@angular/common/http";
 import { waitForAsync, ComponentFixture, TestBed } from "@angular/core/testing";
 import { FormsModule, ReactiveFormsModule } from "@angular/forms";
 import { RouterTestingModule } from "@angular/router/testing";
-import { DeliveryServiceService } from "src/app/services/api";
+import {DeliveryServiceService, UserService} from "src/app/shared/api";
 
 
-import { LinechartDirective } from "../../directives/linechart.directive";
+import { CurrentUserService } from "src/app/shared/currentUser/current-user.service";
+import { LinechartDirective } from "../../shared/charts/linechart.directive";
 import { DeliveryService } from "../../models";
 import { DsCardComponent } from "../ds-card/ds-card.component";
-import { LoadingComponent } from "../loading/loading.component";
-import { TpHeaderComponent } from "../tp-header/tp-header.component";
+import {TpHeaderComponent} from "../../shared/tp-header/tp-header.component";
+import {LoadingComponent} from "../../shared/loading/loading.component";
+import {AlertService} from "../../shared/alert/alert.service";
 import { DashboardComponent } from "./dashboard.component";
 
 describe("DashboardComponent", () => {
@@ -31,6 +33,10 @@ describe("DashboardComponent", () => {
 
 	beforeEach(waitForAsync(() => {
 		const mockAPIService = jasmine.createSpyObj(["getDeliveryServices"]);
+		const mockCurrentUserService = jasmine.createSpyObj(["updateCurrentUser", "login", "logout"],
+			{capabilities: new Set<string>()});
+
+		const mockAlertService = jasmine.createSpyObj(["newAlert"]);
 		mockAPIService.getDeliveryServices.and.returnValue(new Promise(resolve=>resolve([])));
 
 		TestBed.configureTestingModule({
@@ -46,9 +52,14 @@ describe("DashboardComponent", () => {
 				HttpClientModule,
 				ReactiveFormsModule,
 				RouterTestingModule
+			],
+			providers: [
+				{ provide: CurrentUserService, useValue: mockCurrentUserService },
+				{ provide: DeliveryServiceService, useValue: mockAPIService },
+				{ provide: UserService, useValue: mockAPIService },
+				{ provide: AlertService, useValue: mockAlertService }
 			]
 		});
-		TestBed.overrideProvider(DeliveryServiceService, { useValue: mockAPIService });
 		TestBed.compileComponents();
 	}));
 
diff --git a/experimental/traffic-portal/src/app/components/dashboard/dashboard.component.ts b/experimental/traffic-portal/src/app/core/dashboard/dashboard.component.ts
similarity index 95%
rename from experimental/traffic-portal/src/app/components/dashboard/dashboard.component.ts
rename to experimental/traffic-portal/src/app/core/dashboard/dashboard.component.ts
index 77901bc..c5e6f23 100644
--- a/experimental/traffic-portal/src/app/components/dashboard/dashboard.component.ts
+++ b/experimental/traffic-portal/src/app/core/dashboard/dashboard.component.ts
@@ -15,9 +15,9 @@ import { Component, OnInit } from "@angular/core";
 import { FormControl } from "@angular/forms";
 import { ActivatedRoute, Router } from "@angular/router";
 
+import {CurrentUserService} from "src/app/shared/currentUser/current-user.service";
 import { DeliveryService } from "../../models";
-import { AuthenticationService } from "../../services";
-import { DeliveryServiceService } from "../../services/api";
+import { DeliveryServiceService } from "../../shared/api";
 import { orderBy, fuzzyScore } from "../../utils";
 
 /**
@@ -82,7 +82,7 @@ export class DashboardComponent implements OnInit {
 		private readonly dsAPI: DeliveryServiceService,
 		private readonly route: ActivatedRoute,
 		private readonly router: Router,
-		private readonly auth: AuthenticationService
+		private readonly auth: CurrentUserService
 	) {
 		this.now = new Date();
 		this.now.setUTCMilliseconds(0);
diff --git a/experimental/traffic-portal/src/app/components/deliveryservice/deliveryservice.component.html b/experimental/traffic-portal/src/app/core/deliveryservice/deliveryservice.component.html
similarity index 93%
rename from experimental/traffic-portal/src/app/components/deliveryservice/deliveryservice.component.html
rename to experimental/traffic-portal/src/app/core/deliveryservice/deliveryservice.component.html
index 0ee6586..72653aa 100644
--- a/experimental/traffic-portal/src/app/components/deliveryservice/deliveryservice.component.html
+++ b/experimental/traffic-portal/src/app/core/deliveryservice/deliveryservice.component.html
@@ -45,7 +45,7 @@ limitations under the License.
 	 <canvas linechart chartTitle="Transactions at the Edge Tier" [chartDataSets]="tpsChartData" chartYAxisLabel="Transactions Per Second" chartXAxisLabel="Date/Time" [chartDisplayLegend]="true" chartType="time">
 		Your browser does not support canvases or Javascript. Normally, this would be a graph of transaction data.
 	</canvas>
-	<a mat-fab id="invalidate" routerLink="/deliveryservice/{{deliveryservice.id}}/invalidation-jobs" title="invalidate content" aria-label="invalidate content">
+	<a mat-fab id="invalidate" routerLink="/core/deliveryservice/{{deliveryservice.id}}/invalidation-jobs" title="invalidate content" aria-label="invalidate content">
 		<!-- <img src="assets/invalidate.svg" title="Invalidate Content"> -->
 		<fa-icon [icon]="invalidateIcon"></fa-icon>
 	</a>
diff --git a/experimental/traffic-portal/src/app/components/deliveryservice/deliveryservice.component.scss b/experimental/traffic-portal/src/app/core/deliveryservice/deliveryservice.component.scss
similarity index 96%
rename from experimental/traffic-portal/src/app/components/deliveryservice/deliveryservice.component.scss
rename to experimental/traffic-portal/src/app/core/deliveryservice/deliveryservice.component.scss
index c9d7615..0f903bd 100644
--- a/experimental/traffic-portal/src/app/components/deliveryservice/deliveryservice.component.scss
+++ b/experimental/traffic-portal/src/app/core/deliveryservice/deliveryservice.component.scss
@@ -11,7 +11,7 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
-@import '../../../theme.scss';
+@import 'src/theme';
 main {
 	width: 80%;
 	margin: 1em auto 0;
diff --git a/experimental/traffic-portal/src/app/components/deliveryservice/deliveryservice.component.spec.ts b/experimental/traffic-portal/src/app/core/deliveryservice/deliveryservice.component.spec.ts
similarity index 81%
rename from experimental/traffic-portal/src/app/components/deliveryservice/deliveryservice.component.spec.ts
rename to experimental/traffic-portal/src/app/core/deliveryservice/deliveryservice.component.spec.ts
index 45496eb..af3e6ed 100644
--- a/experimental/traffic-portal/src/app/components/deliveryservice/deliveryservice.component.spec.ts
+++ b/experimental/traffic-portal/src/app/core/deliveryservice/deliveryservice.component.spec.ts
@@ -19,10 +19,12 @@ import { RouterTestingModule } from "@angular/router/testing";
 import { of } from "rxjs";
 
 
-import { LinechartDirective } from "../../directives/linechart.directive";
+import { CurrentUserService } from "src/app/shared/currentUser/current-user.service";
+import { LinechartDirective } from "../../shared/charts/linechart.directive";
 import { DeliveryService, GeoLimit, GeoProvider, TPSData } from "../../models";
-import { APIService } from "../../services/api/apiservice";
-import { TpHeaderComponent } from "../tp-header/tp-header.component";
+import {TpHeaderComponent} from "../../shared/tp-header/tp-header.component";
+import {DeliveryServiceService, UserService} from "../../shared/api";
+import {AlertService} from "../../shared/alert/alert.service";
 import { DeliveryserviceComponent } from "./deliveryservice.component";
 
 
@@ -33,6 +35,8 @@ describe("DeliveryserviceComponent", () => {
 	beforeEach(waitForAsync(() => {
 		// mock the API
 		const mockAPIService = jasmine.createSpyObj(["getDeliveryServices", "getDSKBPS", "getAllDSTPSData"]);
+		const mockAlertService = jasmine.createSpyObj(["newAlert"]);
+		const mockCurrentUserService = jasmine.createSpyObj(["updateCurrentUser", "login", "logout"]);
 		mockAPIService.getDeliveryServices.and.returnValue(of({
 			active: true,
 			anonymousBlockingEnabled: false,
@@ -128,9 +132,14 @@ describe("DeliveryserviceComponent", () => {
 				HttpClientModule,
 				ReactiveFormsModule,
 				RouterTestingModule
+			],
+			providers: [
+				{ provide: DeliveryServiceService, useValue: mockAPIService },
+				{ provide: AlertService, useValue: mockAlertService },
+				{ provide: CurrentUserService, useValue: mockCurrentUserService },
+				{ provide: UserService, useValue: mockAPIService }
 			]
 		});
-		TestBed.overrideProvider(APIService, { useValue: mockAPIService });
 		TestBed.compileComponents();
 	}));
 
diff --git a/experimental/traffic-portal/src/app/components/deliveryservice/deliveryservice.component.ts b/experimental/traffic-portal/src/app/core/deliveryservice/deliveryservice.component.ts
similarity index 98%
rename from experimental/traffic-portal/src/app/components/deliveryservice/deliveryservice.component.ts
rename to experimental/traffic-portal/src/app/core/deliveryservice/deliveryservice.component.ts
index 3f67085..792136a 100644
--- a/experimental/traffic-portal/src/app/components/deliveryservice/deliveryservice.component.ts
+++ b/experimental/traffic-portal/src/app/core/deliveryservice/deliveryservice.component.ts
@@ -19,8 +19,8 @@ import { faBroom } from "@fortawesome/free-solid-svg-icons";
 import { Subject } from "rxjs";
 
 import { DataPoint, DataSet, DeliveryService } from "../../models";
-import { AlertService } from "../../services";
-import { DeliveryServiceService } from "../../services/api";
+import { DeliveryServiceService } from "../../shared/api";
+import {AlertService} from "../../shared/alert/alert.service";
 
 /**
  * DeliveryserviceComponent is the controller for a single Delivery Service's
diff --git a/experimental/traffic-portal/src/app/components/ds-card/ds-card.component.html b/experimental/traffic-portal/src/app/core/ds-card/ds-card.component.html
similarity index 97%
rename from experimental/traffic-portal/src/app/components/ds-card/ds-card.component.html
rename to experimental/traffic-portal/src/app/core/ds-card/ds-card.component.html
index 258abc3..323f9ed 100644
--- a/experimental/traffic-portal/src/app/components/ds-card/ds-card.component.html
+++ b/experimental/traffic-portal/src/app/core/ds-card/ds-card.component.html
@@ -57,7 +57,7 @@ limitations under the License.
 		</div>
 	</mat-card-content>
 	<mat-card-actions *ngIf="open">
-		<a mat-raised-button color="accent" class="view-details" routerLink="/deliveryservice/{{deliveryService.id}}" title="View Details" aria-label="View Details">
+		<a mat-raised-button color="accent" class="view-details" routerLink="/core/deliveryservice/{{deliveryService.id}}" title="View Details" aria-label="View Details">
 			View Details
 		</a>
 	</mat-card-actions>
diff --git a/experimental/traffic-portal/src/app/components/ds-card/ds-card.component.scss b/experimental/traffic-portal/src/app/core/ds-card/ds-card.component.scss
similarity index 98%
rename from experimental/traffic-portal/src/app/components/ds-card/ds-card.component.scss
rename to experimental/traffic-portal/src/app/core/ds-card/ds-card.component.scss
index c5d7cab..6e55ac2 100644
--- a/experimental/traffic-portal/src/app/components/ds-card/ds-card.component.scss
+++ b/experimental/traffic-portal/src/app/core/ds-card/ds-card.component.scss
@@ -12,7 +12,7 @@
 * limitations under the License.
 */
 
-@import '../../../theme.scss';
+@import 'src/theme';
 
 mat-card {
 	// border: 2px solid gray;
diff --git a/experimental/traffic-portal/src/app/components/ds-card/ds-card.component.spec.ts b/experimental/traffic-portal/src/app/core/ds-card/ds-card.component.spec.ts
similarity index 85%
rename from experimental/traffic-portal/src/app/components/ds-card/ds-card.component.spec.ts
rename to experimental/traffic-portal/src/app/core/ds-card/ds-card.component.spec.ts
index 44a236c..b5ec330 100644
--- a/experimental/traffic-portal/src/app/components/ds-card/ds-card.component.spec.ts
+++ b/experimental/traffic-portal/src/app/core/ds-card/ds-card.component.spec.ts
@@ -18,10 +18,10 @@ import { RouterTestingModule } from "@angular/router/testing";
 import { of } from "rxjs";
 
 
-import { LinechartDirective } from "../../directives/linechart.directive";
+import { LinechartDirective } from "../../shared/charts/linechart.directive";
 import { DeliveryService } from "../../models";
-import { APIService } from "../../services/api/apiservice";
-import { LoadingComponent } from "../loading/loading.component";
+import {LoadingComponent} from "../../shared/loading/loading.component";
+import {DeliveryServiceService} from "../../shared/api";
 import { DsCardComponent } from "./ds-card.component";
 
 describe("DsCardComponent", () => {
@@ -30,7 +30,7 @@ describe("DsCardComponent", () => {
 
 	beforeEach(waitForAsync(() => {
 		// mock the API
-		const mockAPIService = jasmine.createSpyObj(["getDSKBPS", "getDSCapacity", "getDSHealth"]);
+		const mockAPIService = jasmine.createSpyObj(["getDSKBPS", "getDSCapacity", "getDSHealth", "getDeliveryServices"]);
 		mockAPIService.getDSKBPS.and.returnValue(of([]), of([]));
 		mockAPIService.getDSCapacity.and.returnValue(of({
 			availablePercent: 34,
@@ -51,9 +51,11 @@ describe("DsCardComponent", () => {
 			imports: [
 				HttpClientModule,
 				RouterTestingModule
+			],
+			providers: [
+				{ provide: DeliveryServiceService, useValue: mockAPIService }
 			]
 		});
-		TestBed.overrideProvider(APIService, { useValue: mockAPIService });
 		TestBed.compileComponents();
 	}));
 
diff --git a/experimental/traffic-portal/src/app/components/ds-card/ds-card.component.ts b/experimental/traffic-portal/src/app/core/ds-card/ds-card.component.ts
similarity index 99%
rename from experimental/traffic-portal/src/app/components/ds-card/ds-card.component.ts
rename to experimental/traffic-portal/src/app/core/ds-card/ds-card.component.ts
index f51c13b..08920cd 100644
--- a/experimental/traffic-portal/src/app/components/ds-card/ds-card.component.ts
+++ b/experimental/traffic-portal/src/app/core/ds-card/ds-card.component.ts
@@ -19,7 +19,7 @@ import { faInfoCircle } from "@fortawesome/free-solid-svg-icons";
 import { Subject } from "rxjs";
 
 import { DataPoint, DataSet, DeliveryService, GeoProvider, protocolToString } from "../../models";
-import { DeliveryServiceService } from "../../services/api";
+import { DeliveryServiceService } from "../../shared/api";
 
 
 /**
diff --git a/experimental/traffic-portal/src/app/components/invalidation-jobs/invalidation-jobs.component.html b/experimental/traffic-portal/src/app/core/invalidation-jobs/invalidation-jobs.component.html
similarity index 100%
rename from experimental/traffic-portal/src/app/components/invalidation-jobs/invalidation-jobs.component.html
rename to experimental/traffic-portal/src/app/core/invalidation-jobs/invalidation-jobs.component.html
diff --git a/experimental/traffic-portal/src/app/components/invalidation-jobs/invalidation-jobs.component.scss b/experimental/traffic-portal/src/app/core/invalidation-jobs/invalidation-jobs.component.scss
similarity index 100%
rename from experimental/traffic-portal/src/app/components/invalidation-jobs/invalidation-jobs.component.scss
rename to experimental/traffic-portal/src/app/core/invalidation-jobs/invalidation-jobs.component.scss
diff --git a/experimental/traffic-portal/src/app/components/invalidation-jobs/invalidation-jobs.component.spec.ts b/experimental/traffic-portal/src/app/core/invalidation-jobs/invalidation-jobs.component.spec.ts
similarity index 77%
rename from experimental/traffic-portal/src/app/components/invalidation-jobs/invalidation-jobs.component.spec.ts
rename to experimental/traffic-portal/src/app/core/invalidation-jobs/invalidation-jobs.component.spec.ts
index ff0f81a..2550ca2 100644
--- a/experimental/traffic-portal/src/app/components/invalidation-jobs/invalidation-jobs.component.spec.ts
+++ b/experimental/traffic-portal/src/app/core/invalidation-jobs/invalidation-jobs.component.spec.ts
@@ -18,13 +18,14 @@ import { RouterTestingModule } from "@angular/router/testing";
 import { MatDialogModule } from "@angular/material/dialog";
 
 import { of } from "rxjs";
-import { InvalidationJobService } from "src/app/services/api";
+import {DeliveryServiceService, InvalidationJobService, UserService} from "src/app/shared/api";
 
 
-import { CustomvalidityDirective } from "../../directives/customvalidity.directive";
-import { OpenableDirective } from "../../directives/openable.directive";
+import { CurrentUserService } from "src/app/shared/currentUser/current-user.service";
+import { CustomvalidityDirective } from "../../shared/validation/customvalidity.directive";
+import { OpenableDirective } from "../../shared/openable/openable.directive";
 import { DeliveryService, GeoLimit, GeoProvider, InvalidationJob } from "../../models";
-import { TpHeaderComponent } from "../tp-header/tp-header.component";
+import {TpHeaderComponent} from "../../shared/tp-header/tp-header.component";
 import { InvalidationJobsComponent } from "./invalidation-jobs.component";
 
 describe("InvalidationJobsComponent", () => {
@@ -34,6 +35,7 @@ describe("InvalidationJobsComponent", () => {
 	beforeEach(waitForAsync(() => {
 		// mock the API
 		const mockAPIService = jasmine.createSpyObj(["getInvalidationJobs", "getDeliveryServices"]);
+		const mockCurrentUserService = jasmine.createSpyObj(["updateCurrentUser", "login", "logout"]);
 		mockAPIService.getInvalidationJobs.and.returnValue(of({
 			startTime: new Date(),
 		} as InvalidationJob));
@@ -71,10 +73,15 @@ describe("InvalidationJobsComponent", () => {
 				ReactiveFormsModule,
 				RouterTestingModule,
 				MatDialogModule
+			],
+			providers: [
+				{ provide: InvalidationJobService, useValue: mockAPIService },
+				{ provide: DeliveryServiceService, useValue: mockAPIService },
+				{ provide: UserService, useValue: mockAPIService },
+				{ provide: CurrentUserService, useValue: mockCurrentUserService }
 			]
 		});
 
-		TestBed.overrideProvider(InvalidationJobService, { useValue: mockAPIService });
 		TestBed.compileComponents();
 	}));
 
diff --git a/experimental/traffic-portal/src/app/components/invalidation-jobs/invalidation-jobs.component.ts b/experimental/traffic-portal/src/app/core/invalidation-jobs/invalidation-jobs.component.ts
similarity index 99%
rename from experimental/traffic-portal/src/app/components/invalidation-jobs/invalidation-jobs.component.ts
rename to experimental/traffic-portal/src/app/core/invalidation-jobs/invalidation-jobs.component.ts
index 714dfdd..402e4c6 100644
--- a/experimental/traffic-portal/src/app/components/invalidation-jobs/invalidation-jobs.component.ts
+++ b/experimental/traffic-portal/src/app/core/invalidation-jobs/invalidation-jobs.component.ts
@@ -17,7 +17,7 @@ import { ActivatedRoute } from "@angular/router";
 import { faPlus } from "@fortawesome/free-solid-svg-icons";
 
 import { defaultDeliveryService, DeliveryService, InvalidationJob } from "../../models";
-import { DeliveryServiceService, InvalidationJobService } from "../../services/api";
+import { DeliveryServiceService, InvalidationJobService } from "../../shared/api";
 import { NewInvalidationJobDialogComponent } from "./new-invalidation-job-dialog/new-invalidation-job-dialog.component";
 
 /**
diff --git a/experimental/traffic-portal/src/app/components/invalidation-jobs/new-invalidation-job-dialog/new-invalidation-job-dialog.component.html b/experimental/traffic-portal/src/app/core/invalidation-jobs/new-invalidation-job-dialog/new-invalidation-job-dialog.component.html
similarity index 100%
rename from experimental/traffic-portal/src/app/components/invalidation-jobs/new-invalidation-job-dialog/new-invalidation-job-dialog.component.html
rename to experimental/traffic-portal/src/app/core/invalidation-jobs/new-invalidation-job-dialog/new-invalidation-job-dialog.component.html
diff --git a/experimental/traffic-portal/src/app/components/invalidation-jobs/new-invalidation-job-dialog/new-invalidation-job-dialog.component.scss b/experimental/traffic-portal/src/app/core/invalidation-jobs/new-invalidation-job-dialog/new-invalidation-job-dialog.component.scss
similarity index 100%
rename from experimental/traffic-portal/src/app/components/invalidation-jobs/new-invalidation-job-dialog/new-invalidation-job-dialog.component.scss
rename to experimental/traffic-portal/src/app/core/invalidation-jobs/new-invalidation-job-dialog/new-invalidation-job-dialog.component.scss
diff --git a/experimental/traffic-portal/src/app/components/invalidation-jobs/new-invalidation-job-dialog/new-invalidation-job-dialog.component.spec.ts b/experimental/traffic-portal/src/app/core/invalidation-jobs/new-invalidation-job-dialog/new-invalidation-job-dialog.component.spec.ts
similarity index 86%
rename from experimental/traffic-portal/src/app/components/invalidation-jobs/new-invalidation-job-dialog/new-invalidation-job-dialog.component.spec.ts
rename to experimental/traffic-portal/src/app/core/invalidation-jobs/new-invalidation-job-dialog/new-invalidation-job-dialog.component.spec.ts
index c237b1d..9891bea 100644
--- a/experimental/traffic-portal/src/app/components/invalidation-jobs/new-invalidation-job-dialog/new-invalidation-job-dialog.component.spec.ts
+++ b/experimental/traffic-portal/src/app/core/invalidation-jobs/new-invalidation-job-dialog/new-invalidation-job-dialog.component.spec.ts
@@ -15,6 +15,7 @@ import { HttpClientModule } from "@angular/common/http";
 import { ComponentFixture, TestBed } from "@angular/core/testing";
 import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog";
 
+import {InvalidationJobService} from "../../../shared/api";
 import { NewInvalidationJobDialogComponent } from "./new-invalidation-job-dialog.component";
 
 describe("NewInvalidationJobDialogComponent", () => {
@@ -22,6 +23,7 @@ describe("NewInvalidationJobDialogComponent", () => {
 	let fixture: ComponentFixture<NewInvalidationJobDialogComponent>;
 
 	beforeEach(async () => {
+		const mockAPIService = jasmine.createSpyObj(["getInvalidationJobs"]);
 		await TestBed.configureTestingModule({
 			declarations: [ NewInvalidationJobDialogComponent ],
 			imports: [
@@ -32,7 +34,8 @@ describe("NewInvalidationJobDialogComponent", () => {
 				{provide: MatDialogRef, useValue: {close: (): void => {
 					console.log("dialog closed");
 				}}},
-				{provide: MAT_DIALOG_DATA, useValue: -1}
+				{provide: MAT_DIALOG_DATA, useValue: -1},
+				{ provide: InvalidationJobService, useValue: mockAPIService}
 			]
 		}).compileComponents();
 	});
diff --git a/experimental/traffic-portal/src/app/components/invalidation-jobs/new-invalidation-job-dialog/new-invalidation-job-dialog.component.ts b/experimental/traffic-portal/src/app/core/invalidation-jobs/new-invalidation-job-dialog/new-invalidation-job-dialog.component.ts
similarity index 98%
rename from experimental/traffic-portal/src/app/components/invalidation-jobs/new-invalidation-job-dialog/new-invalidation-job-dialog.component.ts
rename to experimental/traffic-portal/src/app/core/invalidation-jobs/new-invalidation-job-dialog/new-invalidation-job-dialog.component.ts
index 31d828b..49c2ca9 100644
--- a/experimental/traffic-portal/src/app/components/invalidation-jobs/new-invalidation-job-dialog/new-invalidation-job-dialog.component.ts
+++ b/experimental/traffic-portal/src/app/core/invalidation-jobs/new-invalidation-job-dialog/new-invalidation-job-dialog.component.ts
@@ -16,7 +16,7 @@ import { FormControl } from "@angular/forms";
 import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog";
 import { Subject } from "rxjs";
 
-import { InvalidationJobService } from "src/app/services/api";
+import { InvalidationJobService } from "src/app/shared/api";
 
 /**
  * This is the controller for the dialog box that opens when the user creates
diff --git a/experimental/traffic-portal/src/app/components/new-delivery-service/new-delivery-service.component.html b/experimental/traffic-portal/src/app/core/new-delivery-service/new-delivery-service.component.html
similarity index 100%
rename from experimental/traffic-portal/src/app/components/new-delivery-service/new-delivery-service.component.html
rename to experimental/traffic-portal/src/app/core/new-delivery-service/new-delivery-service.component.html
diff --git a/experimental/traffic-portal/src/app/components/new-delivery-service/new-delivery-service.component.scss b/experimental/traffic-portal/src/app/core/new-delivery-service/new-delivery-service.component.scss
similarity index 98%
rename from experimental/traffic-portal/src/app/components/new-delivery-service/new-delivery-service.component.scss
rename to experimental/traffic-portal/src/app/core/new-delivery-service/new-delivery-service.component.scss
index 6d51a21..9f70556 100644
--- a/experimental/traffic-portal/src/app/components/new-delivery-service/new-delivery-service.component.scss
+++ b/experimental/traffic-portal/src/app/core/new-delivery-service/new-delivery-service.component.scss
@@ -11,7 +11,7 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
-@import '../../../theme.scss';
+@import 'src/theme';
 h1, h2 {
 	text-align: center;
 	color: $theme_secondary;
diff --git a/experimental/traffic-portal/src/app/components/new-delivery-service/new-delivery-service.component.spec.ts b/experimental/traffic-portal/src/app/core/new-delivery-service/new-delivery-service.component.spec.ts
similarity index 87%
rename from experimental/traffic-portal/src/app/components/new-delivery-service/new-delivery-service.component.spec.ts
rename to experimental/traffic-portal/src/app/core/new-delivery-service/new-delivery-service.component.spec.ts
index 36a2475..313b1e0 100644
--- a/experimental/traffic-portal/src/app/components/new-delivery-service/new-delivery-service.component.spec.ts
+++ b/experimental/traffic-portal/src/app/core/new-delivery-service/new-delivery-service.component.spec.ts
@@ -24,10 +24,11 @@ import {MatStepperHarness} from "@angular/material/stepper/testing";
 import {NoopAnimationsModule} from "@angular/platform-browser/animations";
 import { RouterTestingModule } from "@angular/router/testing";
 
-import { UserService } from "src/app/services/api";
+import {CDNService, DeliveryServiceService, UserService} from "src/app/shared/api";
 
+import { CurrentUserService } from "src/app/shared/currentUser/current-user.service";
 import { Protocol } from "../../models";
-import { TpHeaderComponent } from "../tp-header/tp-header.component";
+import {TpHeaderComponent} from "../../shared/tp-header/tp-header.component";
 import { NewDeliveryServiceComponent } from "./new-delivery-service.component";
 
 
@@ -39,6 +40,8 @@ describe("NewDeliveryServiceComponent", () => {
 	beforeEach(async () => {
 		// mock the API
 		const mockAPIService = jasmine.createSpyObj(["getRoles", "getCurrentUser"]);
+		const mockCurrentUserService = jasmine.createSpyObj(["updateCurrentUser", "login", "logout"]);
+		mockCurrentUserService.updateCurrentUser.and.returnValue(new Promise(r => r(false)));
 		mockAPIService.getRoles.and.returnValue(new Promise(resolve => resolve([])));
 		mockAPIService.getCurrentUser.and.returnValue(new Promise(resolve => resolve({
 			id: 0,
@@ -61,7 +64,12 @@ describe("NewDeliveryServiceComponent", () => {
 				MatButtonModule,
 				MatRadioModule
 			],
-			providers: [{provide: UserService, useValue: mockAPIService}]
+			providers: [
+				{provide: DeliveryServiceService, useValue: mockAPIService},
+				{provide: CDNService, useValue: mockAPIService},
+				{provide: UserService, useValue: mockAPIService},
+				{ provide: CurrentUserService, useValue: mockCurrentUserService }
+			]
 		}).compileComponents();
 		// TestBed.overrideProvider(UserService, { useValue: mockAPIService });
 		// TestBed.compileComponents();
diff --git a/experimental/traffic-portal/src/app/components/new-delivery-service/new-delivery-service.component.ts b/experimental/traffic-portal/src/app/core/new-delivery-service/new-delivery-service.component.ts
similarity index 98%
rename from experimental/traffic-portal/src/app/components/new-delivery-service/new-delivery-service.component.ts
rename to experimental/traffic-portal/src/app/core/new-delivery-service/new-delivery-service.component.ts
index 93f4596..fa32e5d 100644
--- a/experimental/traffic-portal/src/app/components/new-delivery-service/new-delivery-service.component.ts
+++ b/experimental/traffic-portal/src/app/core/new-delivery-service/new-delivery-service.component.ts
@@ -16,6 +16,7 @@ import { FormControl } from "@angular/forms";
 import type { MatStepper } from "@angular/material/stepper";
 import { Router } from "@angular/router";
 
+import {CurrentUserService} from "src/app/shared/currentUser/current-user.service";
 import {
 	bypassable,
 	CDN,
@@ -26,8 +27,7 @@ import {
 	Type
 } from "../../models";
 import { User } from "../../models/user";
-import { AuthenticationService } from "../../services";
-import { CDNService, DeliveryServiceService } from "../../services/api";
+import { CDNService, DeliveryServiceService } from "../../shared/api";
 
 /**
  * A regular expression that matches character strings that are illegal in `xml_id`s
@@ -126,7 +126,7 @@ export class NewDeliveryServiceComponent implements OnInit {
 	constructor(
 		private readonly dsAPI: DeliveryServiceService,
 		private readonly cdnAPI: CDNService,
-		private readonly auth: AuthenticationService,
+		private readonly auth: CurrentUserService,
 		private readonly router: Router
 	) { }
 
diff --git a/experimental/traffic-portal/src/app/components/servers/server-details/server-details.component.html b/experimental/traffic-portal/src/app/core/servers/server-details/server-details.component.html
similarity index 100%
rename from experimental/traffic-portal/src/app/components/servers/server-details/server-details.component.html
rename to experimental/traffic-portal/src/app/core/servers/server-details/server-details.component.html
diff --git a/experimental/traffic-portal/src/app/components/servers/server-details/server-details.component.scss b/experimental/traffic-portal/src/app/core/servers/server-details/server-details.component.scss
similarity index 98%
rename from experimental/traffic-portal/src/app/components/servers/server-details/server-details.component.scss
rename to experimental/traffic-portal/src/app/core/servers/server-details/server-details.component.scss
index 70f31e8..b502ecb 100644
--- a/experimental/traffic-portal/src/app/components/servers/server-details/server-details.component.scss
+++ b/experimental/traffic-portal/src/app/core/servers/server-details/server-details.component.scss
@@ -11,7 +11,7 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
-@import '../../../../theme.scss';
+@import 'src/theme';
 
 .actions-container {
 	width: 85vw;
diff --git a/experimental/traffic-portal/src/app/components/servers/server-details/server-details.component.spec.ts b/experimental/traffic-portal/src/app/core/servers/server-details/server-details.component.spec.ts
similarity index 52%
rename from experimental/traffic-portal/src/app/components/servers/server-details/server-details.component.spec.ts
rename to experimental/traffic-portal/src/app/core/servers/server-details/server-details.component.spec.ts
index 86069be..69b79be 100644
--- a/experimental/traffic-portal/src/app/components/servers/server-details/server-details.component.spec.ts
+++ b/experimental/traffic-portal/src/app/core/servers/server-details/server-details.component.spec.ts
@@ -17,6 +17,8 @@ import { ComponentFixture, TestBed } from "@angular/core/testing";
 import { FormsModule, ReactiveFormsModule } from "@angular/forms";
 import { RouterTestingModule } from "@angular/router/testing";
 
+import {CacheGroupService, CDNService, ProfileService, ServerService, TypeService} from "../../../shared/api";
+import {PhysicalLocationService} from "../../../shared/api/PhysicalLocationService";
 import { ServerDetailsComponent } from "./server-details.component";
 
 describe("ServerDetailsComponent", () => {
@@ -24,9 +26,27 @@ describe("ServerDetailsComponent", () => {
 	let fixture: ComponentFixture<ServerDetailsComponent>;
 
 	beforeEach(async () => {
+		const mockAPIService = jasmine.createSpyObj(["getServers", "getCacheGroups", "getCDNs",
+			"getProfiles", "getTypes", "getPhysicalLocations", "getStatuses", "getServerTypes"]);
+		mockAPIService.getCacheGroups.and.returnValue(new Promise(r => r([])));
+		mockAPIService.getCDNs.and.returnValue(new Promise(r => r([])));
+		mockAPIService.getStatuses.and.returnValue(new Promise(r => r([])));
+		mockAPIService.getProfiles.and.returnValue(new Promise(r => r([])));
+		mockAPIService.getPhysicalLocations.and.returnValues(new Promise(r => r([])));
+		mockAPIService.getServers.and.returnValues(new Promise(r => r([])));
+		mockAPIService.getServerTypes.and.returnValues(new Promise(r => r([])));
+
 		await TestBed.configureTestingModule({
 			declarations: [ ServerDetailsComponent ],
-			imports: [ HttpClientModule, RouterTestingModule, FormsModule, ReactiveFormsModule ]
+			imports: [ HttpClientModule, RouterTestingModule, FormsModule, ReactiveFormsModule ],
+			 providers: [
+				 { provide: ServerService, useValue: mockAPIService },
+				 { provide: CacheGroupService, useValue: mockAPIService },
+				 { provide: CDNService, useValue: mockAPIService },
+				 { provide: TypeService, useValue: mockAPIService },
+				 { provide: PhysicalLocationService, useValue: mockAPIService },
+				 { provide: ProfileService, useValue: mockAPIService }
+			 ]
 		}).compileComponents();
 	});
 
diff --git a/experimental/traffic-portal/src/app/components/servers/server-details/server-details.component.ts b/experimental/traffic-portal/src/app/core/servers/server-details/server-details.component.ts
similarity index 98%
rename from experimental/traffic-portal/src/app/components/servers/server-details/server-details.component.ts
rename to experimental/traffic-portal/src/app/core/servers/server-details/server-details.component.ts
index 5133f12..2eb2cea 100644
--- a/experimental/traffic-portal/src/app/components/servers/server-details/server-details.component.ts
+++ b/experimental/traffic-portal/src/app/core/servers/server-details/server-details.component.ts
@@ -17,9 +17,9 @@ import { ActivatedRoute, Router } from "@angular/router";
 import { faClock, faMinus, faPlus, faToggleOff, faToggleOn, IconDefinition } from "@fortawesome/free-solid-svg-icons";
 import { faClock as hollowClock } from "@fortawesome/free-regular-svg-icons";
 import { CacheGroup, CDN, DUMMY_SERVER, Interface, PhysicalLocation, Profile, Server, Status, Type } from "src/app/models";
-import { CacheGroupService, CDNService, ProfileService, ServerService, TypeService } from "src/app/services/api";
+import { CacheGroupService, CDNService, ProfileService, ServerService, TypeService } from "src/app/shared/api";
 import { IP, IP_WITH_CIDR } from "src/app/utils";
-import { PhysicalLocationService } from "src/app/services/api/PhysicalLocationService";
+import { PhysicalLocationService } from "src/app/shared/api/PhysicalLocationService";
 
 /**
  * ServerDetailsComponent is the controller for a server's "details" page.
diff --git a/experimental/traffic-portal/src/app/components/servers/servers-table/servers-table.component.html b/experimental/traffic-portal/src/app/core/servers/servers-table/servers-table.component.html
similarity index 100%
rename from experimental/traffic-portal/src/app/components/servers/servers-table/servers-table.component.html
rename to experimental/traffic-portal/src/app/core/servers/servers-table/servers-table.component.html
diff --git a/experimental/traffic-portal/src/app/components/servers/servers-table/servers-table.component.scss b/experimental/traffic-portal/src/app/core/servers/servers-table/servers-table.component.scss
similarity index 100%
rename from experimental/traffic-portal/src/app/components/servers/servers-table/servers-table.component.scss
rename to experimental/traffic-portal/src/app/core/servers/servers-table/servers-table.component.scss
diff --git a/experimental/traffic-portal/src/app/components/servers/servers-table/servers-table.component.spec.ts b/experimental/traffic-portal/src/app/core/servers/servers-table/servers-table.component.spec.ts
similarity index 64%
rename from experimental/traffic-portal/src/app/components/servers/servers-table/servers-table.component.spec.ts
rename to experimental/traffic-portal/src/app/core/servers/servers-table/servers-table.component.spec.ts
index d9d96c7..b1a2942 100644
--- a/experimental/traffic-portal/src/app/components/servers/servers-table/servers-table.component.spec.ts
+++ b/experimental/traffic-portal/src/app/core/servers/servers-table/servers-table.component.spec.ts
@@ -16,7 +16,9 @@ import { HttpClientModule } from "@angular/common/http";
 import { waitForAsync, ComponentFixture, TestBed } from "@angular/core/testing";
 import { RouterTestingModule } from "@angular/router/testing";
 
-import { TpHeaderComponent } from "../../tp-header/tp-header.component";
+import { CurrentUserService } from "src/app/shared/currentUser/current-user.service";
+import {TpHeaderComponent} from "../../../shared/tp-header/tp-header.component";
+import {ServerService, UserService} from "../../../shared/api";
 import { ServersTableComponent } from "./servers-table.component";
 
 
@@ -25,9 +27,17 @@ describe("ServersTableComponent", () => {
 	let fixture: ComponentFixture<ServersTableComponent>;
 
 	beforeEach(waitForAsync(() => {
+		const mockAPIService = jasmine.createSpyObj(["getServers", "getUsers"]);
+		mockAPIService.getServers.and.returnValue(new Promise(r => r([])));
+		const mockCurrentUserService = jasmine.createSpyObj(["updateCurrentUser", "login", "logout"]);
 		TestBed.configureTestingModule({
 			declarations: [ ServersTableComponent, TpHeaderComponent ],
-			imports: [HttpClientModule, RouterTestingModule]
+			imports: [HttpClientModule, RouterTestingModule],
+			providers: [
+				{ provide: ServerService, useValue: mockAPIService },
+				{ provide: UserService, useValue: mockAPIService },
+				{ provide: CurrentUserService, useValue: mockCurrentUserService }
+			]
 		})
 			.compileComponents();
 	}));
diff --git a/experimental/traffic-portal/src/app/components/servers/servers-table/servers-table.component.ts b/experimental/traffic-portal/src/app/core/servers/servers-table/servers-table.component.ts
similarity index 97%
rename from experimental/traffic-portal/src/app/components/servers/servers-table/servers-table.component.ts
rename to experimental/traffic-portal/src/app/core/servers/servers-table/servers-table.component.ts
index 4a47eed..b4d6649 100644
--- a/experimental/traffic-portal/src/app/components/servers/servers-table/servers-table.component.ts
+++ b/experimental/traffic-portal/src/app/core/servers/servers-table/servers-table.component.ts
@@ -20,9 +20,9 @@ import { ITooltipParams } from "ag-grid-community";
 import { BehaviorSubject } from "rxjs";
 
 import { Interface, Server } from "../../../models/server";
-import { ServerService } from "../../../services/api";
+import { ServerService } from "../../../shared/api";
 import { IPV4, serviceInterface } from "../../../utils";
-import { ContextMenuActionEvent, ContextMenuItem } from "../../generic-table/generic-table.component";
+import { ContextMenuActionEvent, ContextMenuItem } from "../../../shared/generic-table/generic-table.component";
 
 /**
  * AugmentedServer has fields that give direct access to its service addresses without needing to recalculate them.
@@ -381,7 +381,7 @@ export class ServersTableComponent implements OnInit {
 		let observables;
 		switch (action.action) {
 			case "viewDetails":
-				this.router.navigate(["/server", (action.data as AugmentedServer).id]);
+				this.router.navigate(["/core/server", (action.data as AugmentedServer).id]);
 				break;
 			case "updateStatus":
 				console.log("'Update Status' clicked - not yet implemented");
diff --git a/experimental/traffic-portal/src/app/components/servers/update-status/update-status.component.html b/experimental/traffic-portal/src/app/core/servers/update-status/update-status.component.html
similarity index 100%
rename from experimental/traffic-portal/src/app/components/servers/update-status/update-status.component.html
rename to experimental/traffic-portal/src/app/core/servers/update-status/update-status.component.html
diff --git a/experimental/traffic-portal/src/app/components/servers/update-status/update-status.component.scss b/experimental/traffic-portal/src/app/core/servers/update-status/update-status.component.scss
similarity index 97%
rename from experimental/traffic-portal/src/app/components/servers/update-status/update-status.component.scss
rename to experimental/traffic-portal/src/app/core/servers/update-status/update-status.component.scss
index 525dec0..f6401cc 100644
--- a/experimental/traffic-portal/src/app/components/servers/update-status/update-status.component.scss
+++ b/experimental/traffic-portal/src/app/core/servers/update-status/update-status.component.scss
@@ -11,7 +11,7 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
-@import "../../../../theme.scss";
+@import "src/theme";
 
 @keyframes appear {
 	from {
diff --git a/experimental/traffic-portal/src/app/components/servers/update-status/update-status.component.spec.ts b/experimental/traffic-portal/src/app/core/servers/update-status/update-status.component.spec.ts
similarity index 80%
rename from experimental/traffic-portal/src/app/components/servers/update-status/update-status.component.spec.ts
rename to experimental/traffic-portal/src/app/core/servers/update-status/update-status.component.spec.ts
index f5da113..6fc5506 100644
--- a/experimental/traffic-portal/src/app/components/servers/update-status/update-status.component.spec.ts
+++ b/experimental/traffic-portal/src/app/core/servers/update-status/update-status.component.spec.ts
@@ -14,6 +14,7 @@
 import { HttpClientModule } from "@angular/common/http";
 import { ComponentFixture, TestBed } from "@angular/core/testing";
 
+import {ServerService} from "../../../shared/api";
 import { UpdateStatusComponent } from "./update-status.component";
 
 describe("UpdateStatusComponent", () => {
@@ -21,9 +22,14 @@ describe("UpdateStatusComponent", () => {
 	let fixture: ComponentFixture<UpdateStatusComponent>;
 
 	beforeEach(async () => {
+		const mockAPIService = jasmine.createSpyObj(["getServers", "getStatuses"]);
+		mockAPIService.getStatuses.and.returnValue(new Promise(r => r([])));
 		await TestBed.configureTestingModule({
 			declarations: [ UpdateStatusComponent ],
-			imports: [ HttpClientModule ]
+			imports: [ HttpClientModule ],
+			providers: [
+				{ provide: ServerService, useValue: mockAPIService }
+			]
 		})
 			.compileComponents();
 	});
diff --git a/experimental/traffic-portal/src/app/components/servers/update-status/update-status.component.ts b/experimental/traffic-portal/src/app/core/servers/update-status/update-status.component.ts
similarity index 98%
rename from experimental/traffic-portal/src/app/components/servers/update-status/update-status.component.ts
rename to experimental/traffic-portal/src/app/core/servers/update-status/update-status.component.ts
index d9c939b..454f808 100644
--- a/experimental/traffic-portal/src/app/components/servers/update-status/update-status.component.ts
+++ b/experimental/traffic-portal/src/app/core/servers/update-status/update-status.component.ts
@@ -14,7 +14,7 @@
 import { Component, EventEmitter, HostListener, Input, OnInit, Output } from "@angular/core";
 import { FormControl } from "@angular/forms";
 import { Server, Status } from "src/app/models";
-import { ServerService } from "src/app/services/api";
+import { ServerService } from "src/app/shared/api";
 
 /**
  * UpdateStatusComponent is the controller for the "Update Server Status" dialog box.
diff --git a/experimental/traffic-portal/src/app/components/users/users.component.html b/experimental/traffic-portal/src/app/core/users/users.component.html
similarity index 100%
rename from experimental/traffic-portal/src/app/components/users/users.component.html
rename to experimental/traffic-portal/src/app/core/users/users.component.html
diff --git a/experimental/traffic-portal/src/app/components/users/users.component.scss b/experimental/traffic-portal/src/app/core/users/users.component.scss
similarity index 97%
rename from experimental/traffic-portal/src/app/components/users/users.component.scss
rename to experimental/traffic-portal/src/app/core/users/users.component.scss
index 1458397..a164053 100644
--- a/experimental/traffic-portal/src/app/components/users/users.component.scss
+++ b/experimental/traffic-portal/src/app/core/users/users.component.scss
@@ -11,7 +11,7 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
-@import '../../../theme.scss';
+@import 'src/theme';
 #users {
 	width: 80%;
 	margin: auto;
diff --git a/experimental/traffic-portal/src/app/components/users/users.component.spec.ts b/experimental/traffic-portal/src/app/core/users/users.component.spec.ts
similarity index 77%
rename from experimental/traffic-portal/src/app/components/users/users.component.spec.ts
rename to experimental/traffic-portal/src/app/core/users/users.component.spec.ts
index d934c78..ccc5244 100644
--- a/experimental/traffic-portal/src/app/components/users/users.component.spec.ts
+++ b/experimental/traffic-portal/src/app/core/users/users.component.spec.ts
@@ -16,10 +16,11 @@ import { waitForAsync, ComponentFixture, TestBed } from "@angular/core/testing";
 import { FormsModule, ReactiveFormsModule } from "@angular/forms";
 import { RouterTestingModule } from "@angular/router/testing";
 
+import { CurrentUserService } from "src/app/shared/currentUser/current-user.service";
 import { User } from "../../models";
-import { APIService } from "../../services/api/apiservice";
-import { LoadingComponent } from "../loading/loading.component";
-import { TpHeaderComponent } from "../tp-header/tp-header.component";
+import {TpHeaderComponent} from "../../shared/tp-header/tp-header.component";
+import {LoadingComponent} from "../../shared/loading/loading.component";
+import {UserService} from "../../shared/api";
 import { UsersComponent } from "./users.component";
 
 describe("UsersComponent", () => {
@@ -36,6 +37,8 @@ describe("UsersComponent", () => {
 			newUser: false,
 			username: "test"
 		} as User)));
+		const mockCurrentUserService = jasmine.createSpyObj(["updateCurrentUser", "login", "logout"]);
+		mockCurrentUserService.updateCurrentUser.and.returnValue(new Promise(r => r(false)));
 
 		TestBed.configureTestingModule({
 			declarations: [
@@ -49,8 +52,11 @@ describe("UsersComponent", () => {
 				ReactiveFormsModule,
 				RouterTestingModule
 			],
+			providers: [
+				{ provide: UserService, useValue: mockAPIService },
+				{ provide: CurrentUserService, useValue: mockCurrentUserService }
+			]
 		});
-		TestBed.overrideProvider(APIService, { useValue: mockAPIService });
 		TestBed.compileComponents();
 	}));
 
diff --git a/experimental/traffic-portal/src/app/components/users/users.component.ts b/experimental/traffic-portal/src/app/core/users/users.component.ts
similarity index 96%
rename from experimental/traffic-portal/src/app/components/users/users.component.ts
rename to experimental/traffic-portal/src/app/core/users/users.component.ts
index 565d08b..e4fe876 100644
--- a/experimental/traffic-portal/src/app/components/users/users.component.ts
+++ b/experimental/traffic-portal/src/app/core/users/users.component.ts
@@ -16,9 +16,9 @@ import { FormControl } from "@angular/forms";
 
 import { BehaviorSubject, Observable } from "rxjs";
 
+import {CurrentUserService} from "src/app/shared/currentUser/current-user.service";
 import { Role, User } from "../../models";
-import { AuthenticationService } from "../../services";
-import { UserService } from "../../services/api";
+import { UserService } from "../../shared/api";
 import { orderBy } from "../../utils";
 
 /**
@@ -53,7 +53,7 @@ export class UsersComponent implements OnInit {
 	/**
 	 * Constructor.
 	 */
-	constructor(private readonly api: UserService, private readonly auth: AuthenticationService) {
+	constructor(private readonly api: UserService, private readonly auth: CurrentUserService) {
 		this.rolesMapSubject = new BehaviorSubject<Map<number, string>>(new Map<number, string>());
 		this.rolesMap = this.rolesMapSubject.asObservable();
 		this.users = new Array<User>();
diff --git a/experimental/traffic-portal/src/app/guards/authenticated-guard.service.ts b/experimental/traffic-portal/src/app/guards/authenticated-guard.service.ts
new file mode 100644
index 0000000..c98a021
--- /dev/null
+++ b/experimental/traffic-portal/src/app/guards/authenticated-guard.service.ts
@@ -0,0 +1,43 @@
+/*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+import {Injectable} from "@angular/core";
+import {CanActivate, CanLoad } from "@angular/router";
+import {CurrentUserService} from "src/app/shared/currentUser/current-user.service";
+
+/**
+ * AuthenticationGuard ensures that the user is logged in.
+ */
+@Injectable()
+export class AuthenticatedGuard implements CanActivate, CanLoad {
+	constructor(private readonly auth: CurrentUserService) {
+	}
+
+	/**
+	 * canActivate determines whether or not a user can activate an already loaded route.
+	 *
+	 * @returns Whether or not the route can be activated.
+	 */
+	public async canActivate(): Promise<boolean>  {
+		return this.auth.fetchCurrentUser();
+	}
+
+	/**
+	 * canLoad determines whether or not the current user can load/request the given route.
+	 *
+	 * @returns Whether or not the route can be loaded.
+	 */
+	public async canLoad(): Promise<boolean> {
+		return this.auth.fetchCurrentUser();
+	}
+}
diff --git a/experimental/traffic-portal/src/app/guards/authentication.guard.spec.ts b/experimental/traffic-portal/src/app/guards/authentication.guard.spec.ts
new file mode 100644
index 0000000..3d1dda8
--- /dev/null
+++ b/experimental/traffic-portal/src/app/guards/authentication.guard.spec.ts
@@ -0,0 +1,39 @@
+/*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+import { TestBed } from "@angular/core/testing";
+
+import { CurrentUserService } from "src/app/shared/currentUser/current-user.service";
+import { AuthenticatedGuard } from "./authenticated-guard.service";
+
+describe("AuthenticationGuard", () => {
+	let guard: AuthenticatedGuard;
+
+	beforeEach(() => {
+		const mockCurrentUserService = jasmine.createSpyObj(["updateCurrentUser", "login", "logout"]);
+		TestBed.configureTestingModule({
+			providers: [
+				{ provide: CurrentUserService, useValue: mockCurrentUserService },
+				AuthenticatedGuard
+			]
+		});
+	});
+
+	beforeEach(() => {
+		guard = TestBed.inject(AuthenticatedGuard);
+	});
+
+	it("should be created", () => {
+		expect(guard).toBeTruthy();
+	});
+});
diff --git a/experimental/traffic-portal/src/app/components/login/login.component.html b/experimental/traffic-portal/src/app/login/login.component.html
similarity index 92%
rename from experimental/traffic-portal/src/app/components/login/login.component.html
rename to experimental/traffic-portal/src/app/login/login.component.html
index a7675cf..acb2346 100644
--- a/experimental/traffic-portal/src/app/components/login/login.component.html
+++ b/experimental/traffic-portal/src/app/login/login.component.html
@@ -12,7 +12,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 -->
 <header>
-	<img src="/assets/logo.svg" alt="Apache Traffic Control logo"/><h1>Traffic Portal</h1>
+	<img src="assets/logo.svg" alt="Apache Traffic Control logo"/><h1>Traffic Portal</h1>
 </header>
 <form name="login" (ngSubmit)="submitLogin()" ngNativeValidate>
 	<label for="u">Username</label><input required autocomplete="username" autofocus type="text" [formControl]="u" name="u" id="u"/>
diff --git a/experimental/traffic-portal/src/app/components/login/login.component.scss b/experimental/traffic-portal/src/app/login/login.component.scss
similarity index 97%
rename from experimental/traffic-portal/src/app/components/login/login.component.scss
rename to experimental/traffic-portal/src/app/login/login.component.scss
index efd36f3..59f2429 100644
--- a/experimental/traffic-portal/src/app/components/login/login.component.scss
+++ b/experimental/traffic-portal/src/app/login/login.component.scss
@@ -11,7 +11,7 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
-@import '../../../theme.scss';
+@import 'src/theme';
 h1 {
 	display: inline-block;
 	color: $theme_tertiary;
diff --git a/experimental/traffic-portal/src/app/components/login/login.component.spec.ts b/experimental/traffic-portal/src/app/login/login.component.spec.ts
similarity index 86%
rename from experimental/traffic-portal/src/app/components/login/login.component.spec.ts
rename to experimental/traffic-portal/src/app/login/login.component.spec.ts
index 55fefdf..a908d2d 100644
--- a/experimental/traffic-portal/src/app/components/login/login.component.spec.ts
+++ b/experimental/traffic-portal/src/app/login/login.component.spec.ts
@@ -16,6 +16,7 @@ import { waitForAsync, ComponentFixture, TestBed } from "@angular/core/testing";
 import { FormsModule, ReactiveFormsModule } from "@angular/forms";
 import { RouterTestingModule } from "@angular/router/testing";
 
+import {CurrentUserService} from "src/app/shared/currentUser/current-user.service";
 import { LoginComponent } from "./login.component";
 
 describe("LoginComponent", () => {
@@ -23,6 +24,7 @@ describe("LoginComponent", () => {
 	let fixture: ComponentFixture<LoginComponent>;
 
 	beforeEach(waitForAsync(() => {
+		const mockCurrentUserService = jasmine.createSpyObj(["updateCurrentUser", "login", "logout"]);
 		TestBed.configureTestingModule({
 			declarations: [ LoginComponent ],
 			imports: [
@@ -30,7 +32,8 @@ describe("LoginComponent", () => {
 				HttpClientModule,
 				ReactiveFormsModule,
 				RouterTestingModule
-			]
+			],
+			providers: [ { provide: CurrentUserService, useValue: mockCurrentUserService }]
 		})
 			.compileComponents();
 	}));
diff --git a/experimental/traffic-portal/src/app/components/login/login.component.ts b/experimental/traffic-portal/src/app/login/login.component.ts
similarity index 90%
rename from experimental/traffic-portal/src/app/components/login/login.component.ts
rename to experimental/traffic-portal/src/app/login/login.component.ts
index 1e7f936..de7c2c7 100644
--- a/experimental/traffic-portal/src/app/components/login/login.component.ts
+++ b/experimental/traffic-portal/src/app/login/login.component.ts
@@ -14,8 +14,8 @@
 import { Component, OnInit } from "@angular/core";
 import { FormControl } from "@angular/forms";
 import { Router, ActivatedRoute } from "@angular/router";
+import {CurrentUserService} from "src/app/shared/currentUser/current-user.service";
 
-import { AuthenticationService } from "../../services";
 
 /**
  * LoginComponent is the controller for the user login form.
@@ -40,20 +40,20 @@ export class LoginComponent implements OnInit {
 	constructor(
 		private readonly route: ActivatedRoute,
 		private readonly router: Router,
-		private readonly auth: AuthenticationService) { }
+		private readonly auth: CurrentUserService) { }
 
 	/**
 	 * Runs initialization, setting up the post-login redirection from the query
 	 * string parameters.
 	 */
 	public ngOnInit(): void {
-		this.returnURL = this.route.snapshot.queryParams.returnUrl || "/";
+		this.returnURL = this.route.snapshot.queryParams.returnUrl || "/core";
 		const token = this.route.snapshot.queryParams.token;
 		if (token) {
 			this.auth.login(token).then(
 				response => {
 					if (response) {
-						this.router.navigate(["/me"], {queryParams: {edit: true, updatePassword: true}});
+						this.router.navigate(["/core/me"], {queryParams: {edit: true, updatePassword: true}});
 					}
 				},
 				err => {
diff --git a/experimental/traffic-portal/src/app/models/alert.ts b/experimental/traffic-portal/src/app/models/alert.model.ts
similarity index 100%
rename from experimental/traffic-portal/src/app/models/alert.ts
rename to experimental/traffic-portal/src/app/models/alert.model.ts
diff --git a/experimental/traffic-portal/src/app/models/index.ts b/experimental/traffic-portal/src/app/models/index.ts
index 527344b..d7e35f2 100644
--- a/experimental/traffic-portal/src/app/models/index.ts
+++ b/experimental/traffic-portal/src/app/models/index.ts
@@ -11,7 +11,7 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
-export * from "./alert";
+export * from "./alert.model";
 export * from "./cache-groups";
 export * from "./cdn";
 export * from "./data";
diff --git a/experimental/traffic-portal/src/app/services/authentication.service.ts b/experimental/traffic-portal/src/app/services/authentication.service.ts
deleted file mode 100644
index 5abf69d..0000000
--- a/experimental/traffic-portal/src/app/services/authentication.service.ts
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-import { Injectable } from "@angular/core";
-import { User } from "../models";
-
-import { UserService } from "./api";
-import { CurrentUserService } from "./current-user.service";
-
-/**
- * AuthenticationService handles authentication with the Traffic Ops server and
- * providing properties of the current user to service consumers.
- */
-@Injectable({ providedIn: "root" })
-export class AuthenticationService {
-
-	/**
-	 * The currently authenticated user - or `null` if not authenticated.
-	 */
-	public get currentUser(): User | null {
-		return this.currentUserService.currentUser;
-	}
-
-	/**
-	 * All of the Permissions afforded to the currently authenticated user.
-	 */
-	public get capabilities(): Set<string> {
-		return this.currentUserService.capabilities;
-	}
-
-	/**
-	 * Constructs the service with its required dependencies injected.
-	 *
-	 * @param api A reference to the UserService.
-	 */
-	constructor(private readonly api: UserService, private readonly currentUserService: CurrentUserService) {
-		this.updateCurrentUser();
-	}
-
-	/**
-	 * Updates the current user, and provides a way for callers to check if the update was succesful.
-	 *
-	 * @param token If given, the service will first attempt to login using this token.
-	 * @returns A boolean value indicating the success of the update
-	 */
-	public async updateCurrentUser(token?: string): Promise<boolean> {
-		if (token) {
-			if (!(await this.api.login(token))) {
-				console.error("invalid token");
-				return false;
-			}
-		}
-		return this.api.getCurrentUser().then(
-			async u => {
-				if (u.role === undefined) {
-					throw new Error("current user had no Role");
-				}
-				const role = await this.api.getRoles(u.role);
-				this.currentUserService.setUser(u, new Set(role.capabilities));
-				return true;
-			}
-		).catch(
-			e => {
-				console.error("Failed to update current user:", e);
-				return false;
-			}
-		);
-	}
-
-	/**
-	 * Logs in a user and, on successful login, updates the current user.
-	 *
-	 * @param uOrT The user's username, if `p` is given. If `p` is *not* given,
-	 * this is treated as a login token.
-	 * @param p The user's password.
-	 * @returns An observable that emits whether or not login succeeded.
-	 */
-	public async login(uOrT: string, p?: string): Promise<boolean> {
-		return this.api.login(uOrT, p).then(
-			async resp => {
-				if (resp && resp.status === 200) {
-					return this.updateCurrentUser();
-				}
-				return false;
-			}
-		);
-	}
-
-	/** Logs the currently logged-in user out. */
-	public logout(): void {
-		this.currentUserService.logout();
-	}
-}
diff --git a/experimental/traffic-portal/src/app/services/index.ts b/experimental/traffic-portal/src/app/services/index.ts
deleted file mode 100644
index bf66d56..0000000
--- a/experimental/traffic-portal/src/app/services/index.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-export * from "./authentication.service";
-export * from "./alert.service";
-export * from "./current-user.service";
diff --git a/experimental/traffic-portal/src/app/components/alert/alert.component.html b/experimental/traffic-portal/src/app/shared/alert/alert.component.html
similarity index 100%
rename from experimental/traffic-portal/src/app/components/alert/alert.component.html
rename to experimental/traffic-portal/src/app/shared/alert/alert.component.html
diff --git a/experimental/traffic-portal/src/app/components/alert/alert.component.scss b/experimental/traffic-portal/src/app/shared/alert/alert.component.scss
similarity index 100%
rename from experimental/traffic-portal/src/app/components/alert/alert.component.scss
rename to experimental/traffic-portal/src/app/shared/alert/alert.component.scss
diff --git a/experimental/traffic-portal/src/app/components/alert/alert.component.spec.ts b/experimental/traffic-portal/src/app/shared/alert/alert.component.spec.ts
similarity index 92%
rename from experimental/traffic-portal/src/app/components/alert/alert.component.spec.ts
rename to experimental/traffic-portal/src/app/shared/alert/alert.component.spec.ts
index c162e3f..3353386 100644
--- a/experimental/traffic-portal/src/app/components/alert/alert.component.spec.ts
+++ b/experimental/traffic-portal/src/app/shared/alert/alert.component.spec.ts
@@ -18,8 +18,8 @@ import {HarnessLoader} from "@angular/cdk/testing";
 import {MatSnackBarModule} from "@angular/material/snack-bar";
 import {NoopAnimationsModule} from "@angular/platform-browser/animations";
 
-import { AlertService } from "src/app/services";
-import { AlertLevel } from "src/app/models";
+import { AlertLevel } from "../../models/alert.model";
+import { AlertService } from "./alert.service";
 import { AlertComponent } from "./alert.component";
 
 describe("AlertComponent", () => {
@@ -31,14 +31,15 @@ describe("AlertComponent", () => {
 	beforeEach(async () => {
 		await TestBed.configureTestingModule({
 			declarations: [ AlertComponent ],
-			imports: [MatSnackBarModule, NoopAnimationsModule]
+			imports: [MatSnackBarModule, NoopAnimationsModule],
+			providers: [ AlertService ]
 		}).compileComponents();
+		service = TestBed.inject(AlertService);
 		fixture = TestBed.createComponent(AlertComponent);
 		component = fixture.componentInstance;
 		fixture.detectChanges();
 		fixture.componentInstance.duration = undefined;
 		loader = TestbedHarnessEnvironment.documentRootLoader(fixture);
-		service = TestBed.inject(AlertService);
 	});
 
 	it("should exist", () => {
diff --git a/experimental/traffic-portal/src/app/components/alert/alert.component.ts b/experimental/traffic-portal/src/app/shared/alert/alert.component.ts
similarity index 97%
rename from experimental/traffic-portal/src/app/components/alert/alert.component.ts
rename to experimental/traffic-portal/src/app/shared/alert/alert.component.ts
index f3670a0..7259c04 100644
--- a/experimental/traffic-portal/src/app/components/alert/alert.component.ts
+++ b/experimental/traffic-portal/src/app/shared/alert/alert.component.ts
@@ -15,7 +15,7 @@ import { MatSnackBar } from "@angular/material/snack-bar";
 import { Component, OnDestroy } from "@angular/core";
 import { Subscription } from "rxjs";
 
-import { AlertService } from "../../services";
+import { AlertService } from "./alert.service";
 
 /**
  * AlertComponent is the controller for alert message popups.
diff --git a/experimental/traffic-portal/src/app/services/alert.service.ts b/experimental/traffic-portal/src/app/shared/alert/alert.service.ts
similarity index 95%
rename from experimental/traffic-portal/src/app/services/alert.service.ts
rename to experimental/traffic-portal/src/app/shared/alert/alert.service.ts
index 77edae8..50922e6 100644
--- a/experimental/traffic-portal/src/app/services/alert.service.ts
+++ b/experimental/traffic-portal/src/app/shared/alert/alert.service.ts
@@ -15,13 +15,13 @@ import { Injectable } from "@angular/core";
 
 import { BehaviorSubject, Observable } from "rxjs";
 
-import { Alert, AlertLevel } from "../models/alert";
+import { Alert, AlertLevel } from "../../models/alert.model";
 
 /**
  * This class is responsible for populating an alerts Observable that can be
  * subscribed to by the `AlertComponent`.
  */
-@Injectable({ providedIn: "root" })
+@Injectable()
 export class AlertService {
 	/** A BehaviorSubject that emits Alerts. */
 	public alertsSubject: BehaviorSubject<Alert | null>;
diff --git a/experimental/traffic-portal/src/app/services/api/apiservice.ts b/experimental/traffic-portal/src/app/shared/api/APIService.ts
similarity index 100%
rename from experimental/traffic-portal/src/app/services/api/apiservice.ts
rename to experimental/traffic-portal/src/app/shared/api/APIService.ts
diff --git a/experimental/traffic-portal/src/app/services/api/CDNService.ts b/experimental/traffic-portal/src/app/shared/api/CDNService.ts
similarity index 96%
rename from experimental/traffic-portal/src/app/services/api/CDNService.ts
rename to experimental/traffic-portal/src/app/shared/api/CDNService.ts
index f9d9334..50c85e5 100644
--- a/experimental/traffic-portal/src/app/services/api/CDNService.ts
+++ b/experimental/traffic-portal/src/app/shared/api/CDNService.ts
@@ -15,13 +15,13 @@ import { HttpClient } from "@angular/common/http";
 import { Injectable } from "@angular/core";
 
 import { CDN } from "../../models";
-import { APIService } from "./apiservice";
+import { APIService } from "./APIService";
 
 
 /**
  * CDNService expose API functionality relating to CDNs.
  */
-@Injectable({providedIn: "root"})
+@Injectable()
 export class CDNService extends APIService {
 	public async getCDNs(id: number): Promise<CDN>;
 	public async getCDNs(): Promise<Map<string, CDN>>;
diff --git a/experimental/traffic-portal/src/app/services/api/CacheGroupService.ts b/experimental/traffic-portal/src/app/shared/api/CacheGroupService.ts
similarity index 97%
rename from experimental/traffic-portal/src/app/services/api/CacheGroupService.ts
rename to experimental/traffic-portal/src/app/shared/api/CacheGroupService.ts
index 05425e0..04d91bf 100644
--- a/experimental/traffic-portal/src/app/services/api/CacheGroupService.ts
+++ b/experimental/traffic-portal/src/app/shared/api/CacheGroupService.ts
@@ -15,13 +15,13 @@ import { HttpClient } from "@angular/common/http";
 import { Injectable } from "@angular/core";
 
 import { CacheGroup } from "src/app/models";
-import { APIService } from "./apiservice";
+import { APIService } from "./APIService";
 
 
 /**
  * CDNService expose API functionality relating to CDNs.
  */
-@Injectable({providedIn: "root"})
+@Injectable()
 export class CacheGroupService extends APIService {
 	public async getCacheGroups(idOrName: number | string): Promise<CacheGroup>;
 	public async getCacheGroups(): Promise<Array<CacheGroup>>;
diff --git a/experimental/traffic-portal/src/app/services/api/DeliveryServiceService.ts b/experimental/traffic-portal/src/app/shared/api/DeliveryServiceService.ts
similarity index 99%
rename from experimental/traffic-portal/src/app/services/api/DeliveryServiceService.ts
rename to experimental/traffic-portal/src/app/shared/api/DeliveryServiceService.ts
index 5591e18..774e034 100644
--- a/experimental/traffic-portal/src/app/services/api/DeliveryServiceService.ts
+++ b/experimental/traffic-portal/src/app/shared/api/DeliveryServiceService.ts
@@ -26,7 +26,7 @@ import {
 	TPSData,
 	Type
 } from "../../models";
-import { APIService } from "./apiservice";
+import { APIService } from "./APIService";
 
 /**
  * The type of a raw response returned from the API that has to be massaged
@@ -141,7 +141,7 @@ function constructDataSetFromResponse(r: object): DataSetWithSummary {
 /**
  * DeliveryServiceService exposes API functionality related to Delivery Services.
  */
-@Injectable({providedIn: "root"})
+@Injectable()
 export class DeliveryServiceService extends APIService {
 
 	/** This is where DS Types are cached, as they are presumed to not change (often). */
diff --git a/experimental/traffic-portal/src/app/services/api/InvalidationJobService.ts b/experimental/traffic-portal/src/app/shared/api/InvalidationJobService.ts
similarity index 97%
rename from experimental/traffic-portal/src/app/services/api/InvalidationJobService.ts
rename to experimental/traffic-portal/src/app/shared/api/InvalidationJobService.ts
index 8ca5361..0737303 100644
--- a/experimental/traffic-portal/src/app/services/api/InvalidationJobService.ts
+++ b/experimental/traffic-portal/src/app/shared/api/InvalidationJobService.ts
@@ -17,7 +17,7 @@ import { Injectable } from "@angular/core";
 
 import { DeliveryService, InvalidationJob, NewInvalidationJob, User } from "../../models";
 
-import { APIService } from "./apiservice";
+import { APIService } from "./APIService";
 
 /**
  * JobOpts defines the options that can be passed to getInvalidationJobs.
@@ -38,7 +38,7 @@ interface JobOpts {
 /**
  * InvalidationJobService exposes API functionality related to Content Invalidation Jobs.
  */
-@Injectable({providedIn: "root"})
+@Injectable()
 export class InvalidationJobService extends APIService {
 
 	/**
diff --git a/experimental/traffic-portal/src/app/services/api/PhysicalLocationService.ts b/experimental/traffic-portal/src/app/shared/api/PhysicalLocationService.ts
similarity index 96%
rename from experimental/traffic-portal/src/app/services/api/PhysicalLocationService.ts
rename to experimental/traffic-portal/src/app/shared/api/PhysicalLocationService.ts
index ab5bd7e..f5ad89d 100644
--- a/experimental/traffic-portal/src/app/services/api/PhysicalLocationService.ts
+++ b/experimental/traffic-portal/src/app/shared/api/PhysicalLocationService.ts
@@ -15,12 +15,12 @@ import { HttpClient } from "@angular/common/http";
 import { Injectable } from "@angular/core";
 
 import { PhysicalLocation } from "../../models";
-import { APIService } from "./apiservice";
+import { APIService } from "./APIService";
 
 /**
  * PhysicalLocationService exposes API functionality relating to PhysicalLocations.
  */
-@Injectable({providedIn: "root"})
+@Injectable()
 export class PhysicalLocationService extends APIService {
 	public async getPhysicalLocations(idOrName: number | string): Promise<PhysicalLocation>;
 	public async getPhysicalLocations(): Promise<Array<PhysicalLocation>>;
diff --git a/experimental/traffic-portal/src/app/services/api/ProfileService.ts b/experimental/traffic-portal/src/app/shared/api/ProfileService.ts
similarity index 97%
rename from experimental/traffic-portal/src/app/services/api/ProfileService.ts
rename to experimental/traffic-portal/src/app/shared/api/ProfileService.ts
index 6986406..328cc57 100644
--- a/experimental/traffic-portal/src/app/services/api/ProfileService.ts
+++ b/experimental/traffic-portal/src/app/shared/api/ProfileService.ts
@@ -17,7 +17,7 @@ import { Injectable } from "@angular/core";
 
 import { Parameter, Profile } from "../../models";
 
-import { APIService } from "./apiservice";
+import { APIService } from "./APIService";
 
 /**
  * Shared mapping function for converting Parameter 'lastUpdated' fields to actual dates.
@@ -51,7 +51,7 @@ function profileMap(p: Profile): Profile {
 /**
  * ServerService exposes API functionality related to Servers.
  */
-@Injectable({providedIn: "root"})
+@Injectable()
 export class ProfileService extends APIService {
 
 	/**
diff --git a/experimental/traffic-portal/src/app/services/api/ServerService.ts b/experimental/traffic-portal/src/app/shared/api/ServerService.ts
similarity index 98%
rename from experimental/traffic-portal/src/app/services/api/ServerService.ts
rename to experimental/traffic-portal/src/app/shared/api/ServerService.ts
index 448bd46..aae80e2 100644
--- a/experimental/traffic-portal/src/app/services/api/ServerService.ts
+++ b/experimental/traffic-portal/src/app/shared/api/ServerService.ts
@@ -17,7 +17,7 @@ import { Injectable } from "@angular/core";
 
 import { defaultServer, Server, Servercheck, Status } from "../../models";
 
-import { APIService } from "./apiservice";
+import { APIService } from "./APIService";
 
 /**
  * Shared mapping for massaging Status requests.
@@ -50,7 +50,7 @@ function serverMap(s: Server): Server {
 /**
  * ServerService exposes API functionality related to Servers.
  */
-@Injectable({providedIn: "root"})
+@Injectable()
 export class ServerService extends APIService {
 
 	/**
diff --git a/experimental/traffic-portal/src/app/services/api/TypeService.ts b/experimental/traffic-portal/src/app/shared/api/TypeService.ts
similarity index 97%
rename from experimental/traffic-portal/src/app/services/api/TypeService.ts
rename to experimental/traffic-portal/src/app/shared/api/TypeService.ts
index ee4f29f..ee56d89 100644
--- a/experimental/traffic-portal/src/app/services/api/TypeService.ts
+++ b/experimental/traffic-portal/src/app/shared/api/TypeService.ts
@@ -15,7 +15,7 @@ import { HttpClient } from "@angular/common/http";
 import { Injectable } from "@angular/core";
 
 import { Type } from "../../models";
-import { APIService } from "./apiservice";
+import { APIService } from "./APIService";
 
 /** The allowed values for the 'useInTables' query parameter of GET requests to /types. */
 type UseInTable = "cachegroup" |
@@ -30,7 +30,7 @@ type UseInTable = "cachegroup" |
 /**
  * TypeService exposes API functionality relating to Types.
  */
-@Injectable({providedIn: "root"})
+@Injectable()
 export class TypeService extends APIService {
 	public async getTypes(idOrName: number | string): Promise<Type>;
 	public async getTypes(): Promise<Array<Type>>;
diff --git a/experimental/traffic-portal/src/app/services/api/UserService.ts b/experimental/traffic-portal/src/app/shared/api/UserService.ts
similarity index 97%
rename from experimental/traffic-portal/src/app/services/api/UserService.ts
rename to experimental/traffic-portal/src/app/shared/api/UserService.ts
index d4afb0c..e765afb 100644
--- a/experimental/traffic-portal/src/app/services/api/UserService.ts
+++ b/experimental/traffic-portal/src/app/shared/api/UserService.ts
@@ -17,12 +17,12 @@ import { Injectable } from "@angular/core";
 
 import { Role, User, Capability } from "../../models/user";
 
-import { APIService } from "./apiservice";
+import { APIService } from "./APIService";
 
 /**
  * UserService exposes API functionality related to Users, Roles and Capabilities.
  */
-@Injectable({providedIn: "root"})
+@Injectable()
 export class UserService extends APIService {
 
 	/**
@@ -63,7 +63,7 @@ export class UserService extends APIService {
 
 	/**
 	 * Ends the current user's session - but does *not* affect the
-	 * AuthenticationService's user data, which must be separately cleared.
+	 * CurrentUserService's user data, which must be separately cleared.
 	 *
 	 * @returns The entire HTTP response on succes, or `null` on failure.
 	 */
diff --git a/experimental/traffic-portal/src/app/services/api/index.ts b/experimental/traffic-portal/src/app/shared/api/index.ts
similarity index 100%
rename from experimental/traffic-portal/src/app/services/api/index.ts
rename to experimental/traffic-portal/src/app/shared/api/index.ts
diff --git a/experimental/traffic-portal/src/app/directives/linechart.directive.spec.ts b/experimental/traffic-portal/src/app/shared/charts/linechart.directive.spec.ts
similarity index 100%
rename from experimental/traffic-portal/src/app/directives/linechart.directive.spec.ts
rename to experimental/traffic-portal/src/app/shared/charts/linechart.directive.spec.ts
diff --git a/experimental/traffic-portal/src/app/directives/linechart.directive.ts b/experimental/traffic-portal/src/app/shared/charts/linechart.directive.ts
similarity index 99%
rename from experimental/traffic-portal/src/app/directives/linechart.directive.ts
rename to experimental/traffic-portal/src/app/shared/charts/linechart.directive.ts
index 1094062..e6ffb82 100644
--- a/experimental/traffic-portal/src/app/directives/linechart.directive.ts
+++ b/experimental/traffic-portal/src/app/shared/charts/linechart.directive.ts
@@ -17,7 +17,7 @@ import { from, Observable, Subscription } from "rxjs";
 
 import { Chart } from "chart.js"; // TODO: use plotly instead for WebGL-capabale browsers?
 
-import { DataSet } from "../models/data";
+import { DataSet } from "../../models/data";
 
 /**
  * LineChartType enumerates the valid types of charts.
diff --git a/experimental/traffic-portal/src/app/services/current-user.service.spec.ts b/experimental/traffic-portal/src/app/shared/currentUser/current-user.service.spec.ts
similarity index 85%
rename from experimental/traffic-portal/src/app/services/current-user.service.spec.ts
rename to experimental/traffic-portal/src/app/shared/currentUser/current-user.service.spec.ts
index 7561709..3e994b2 100644
--- a/experimental/traffic-portal/src/app/services/current-user.service.spec.ts
+++ b/experimental/traffic-portal/src/app/shared/currentUser/current-user.service.spec.ts
@@ -13,7 +13,9 @@
 */
 import { TestBed } from "@angular/core/testing";
 import { RouterTestingModule } from "@angular/router/testing";
-import { LoginComponent } from "../components/login/login.component";
+import {UserService} from "src/app/shared/api";
+import {User} from "src/app/models";
+import { LoginComponent } from "../../login/login.component";
 
 import { CurrentUserService } from "./current-user.service";
 
@@ -21,8 +23,17 @@ describe("CurrentUserService", () => {
 	let service: CurrentUserService;
 
 	beforeEach(() => {
+		const mockAPIService = jasmine.createSpyObj(["getRoles", "updateCurrentUser", "getCurrentUser", "saveCurrentUser"]);
+		mockAPIService.getCurrentUser.and.returnValue(new Promise<User>(resolve => resolve(
+			{id: 1, newUser: false, role: 1, username: "name"}
+		)));
+		mockAPIService.getRoles.and.returnValue(new Promise(resolve => resolve([])));
 		TestBed.configureTestingModule({
-			imports: [RouterTestingModule.withRoutes([{component: LoginComponent, path: "login"}])]
+			imports: [RouterTestingModule.withRoutes([{component: LoginComponent, path: "login"}])],
+			providers: [
+				CurrentUserService,
+				{provide: UserService, useValue: mockAPIService}
+			]
 		});
 		service = TestBed.inject(CurrentUserService);
 	});
diff --git a/experimental/traffic-portal/src/app/services/current-user.service.ts b/experimental/traffic-portal/src/app/shared/currentUser/current-user.service.ts
similarity index 50%
rename from experimental/traffic-portal/src/app/services/current-user.service.ts
rename to experimental/traffic-portal/src/app/shared/currentUser/current-user.service.ts
index 2a3645f..f0512f4 100644
--- a/experimental/traffic-portal/src/app/services/current-user.service.ts
+++ b/experimental/traffic-portal/src/app/shared/currentUser/current-user.service.ts
@@ -11,22 +11,25 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
-import { Injectable } from "@angular/core";
+import {EventEmitter, Injectable} from "@angular/core";
 import { Router } from "@angular/router";
-import { Capability, User } from "../models";
+import {UserService} from "src/app/shared/api";
+import { Capability, User } from "../../models";
 
 /**
  * This service keeps track of the currently authenticated user.
  *
- * This needs to be done separately from the AuthenticationService's
+ * This needs to be done separately from the CurrentUserService's
  * methods, because those depend on the API services and the API services use
  * an implicitly injected ErrorInterceptor which clears the authenticated user
  * value when it hits a 401 error - so that would be a circular dependency.
  */
-@Injectable({
-	providedIn: "root"
-})
+@Injectable()
 export class CurrentUserService {
+	/** Makes updateCurrentUser able to be called from multiple places without regard to order */
+	private updatingUserPromise: Promise<boolean> | null = null;
+	/** To allow downstream code to stay up to date with the current user */
+	public userChanged: EventEmitter<User> = new EventEmitter<User>();
 	/** The currently authenticated user - or `null` if not authenticated. */
 	private user: User | null = null;
 	/** The Permissions afforded to the currently authenticated user. */
@@ -46,7 +49,76 @@ export class CurrentUserService {
 		return this.currentUser !== null;
 	}
 
-	constructor(private readonly router: Router) {}
+	constructor(private readonly router: Router, private readonly api: UserService) {
+		this.updateCurrentUser();
+	}
+
+	/**
+	 * Gets the current user if currentuser is not already set
+	 *
+	 * @returns A promise containing the value indicating the success of the update
+	 */
+	public async fetchCurrentUser(): Promise<boolean> {
+		if(this.currentUser !== null){
+			return new Promise<boolean>(resolve => resolve(true));
+		}
+		return this.updateCurrentUser();
+	}
+
+	/**
+	 * Updates the current user, and provides a way for callers to check if the update was successful.
+	 *
+	 * @returns A boolean value indicating the success of the update
+	 */
+	public async updateCurrentUser(): Promise<boolean> {
+		if (this.updatingUserPromise === null) {
+			this.updatingUserPromise = this.api.getCurrentUser().then(
+				async u => {
+					if (u.role === undefined) {
+						throw new Error("current user had no Role");
+					}
+					const role = await this.api.getRoles(u.role);
+					this.setUser(u, new Set(role.capabilities));
+					return true;
+				}
+			).catch(
+				e => {
+					console.error("Failed to update current user:", e);
+					return false;
+				}
+			).finally(() => this.updatingUserPromise = null );
+		}
+		return this.updatingUserPromise;
+	}
+
+	/**
+	 * Saves the user
+	 *
+	 * @param user User to e saved
+	 * @returns A promise returning the status of the update.
+	 */
+	public async saveCurrentUser(user: User): Promise<boolean> {
+		return this.api.updateCurrentUser(user);
+	}
+
+	/**
+	 * Logs in a user and, on successful login, updates the current user.
+	 *
+	 * @param uOrT The user's username, if `p` is given. If `p` is *not* given,
+	 * this is treated as a login token.
+	 * @param p The user's password.
+	 * @returns An observable that emits whether or not login succeeded.
+	 */
+	public async login(uOrT: string, p?: string): Promise<boolean> {
+		return this.api.login(uOrT, p).then(
+			async resp => {
+				if (resp && resp.status === 200) {
+					return this.updateCurrentUser();
+				}
+				return false;
+			}
+		);
+	}
 
 	/**
 	 * Sets the currently authenticated user.
@@ -57,6 +129,7 @@ export class CurrentUserService {
 	public setUser(u: User, caps: Set<string> | Array<Capability>): void {
 		this.user = u;
 		this.caps = caps instanceof Array ? new Set(caps.map(c=>c.name)) : caps;
+		this.userChanged.emit(this.user);
 	}
 
 	/**
@@ -83,6 +156,9 @@ export class CurrentUserService {
 		const queryParams: Record<string | symbol, string> = {};
 		if (withRedirect) {
 			queryParams.returnUrl = this.router.url;
+			if (queryParams.returnUrl.startsWith("/login")) {
+				queryParams.returnUrl = "/core";
+			}
 		}
 		console.log("query params:", queryParams);
 		this.router.navigate(["/login"], {queryParams});
diff --git a/experimental/traffic-portal/src/app/components/generic-table/generic-table.component.html b/experimental/traffic-portal/src/app/shared/generic-table/generic-table.component.html
similarity index 100%
rename from experimental/traffic-portal/src/app/components/generic-table/generic-table.component.html
rename to experimental/traffic-portal/src/app/shared/generic-table/generic-table.component.html
diff --git a/experimental/traffic-portal/src/app/components/generic-table/generic-table.component.scss b/experimental/traffic-portal/src/app/shared/generic-table/generic-table.component.scss
similarity index 100%
rename from experimental/traffic-portal/src/app/components/generic-table/generic-table.component.scss
rename to experimental/traffic-portal/src/app/shared/generic-table/generic-table.component.scss
diff --git a/experimental/traffic-portal/src/app/components/generic-table/generic-table.component.spec.ts b/experimental/traffic-portal/src/app/shared/generic-table/generic-table.component.spec.ts
similarity index 100%
rename from experimental/traffic-portal/src/app/components/generic-table/generic-table.component.spec.ts
rename to experimental/traffic-portal/src/app/shared/generic-table/generic-table.component.spec.ts
diff --git a/experimental/traffic-portal/src/app/components/generic-table/generic-table.component.ts b/experimental/traffic-portal/src/app/shared/generic-table/generic-table.component.ts
similarity index 100%
rename from experimental/traffic-portal/src/app/components/generic-table/generic-table.component.ts
rename to experimental/traffic-portal/src/app/shared/generic-table/generic-table.component.ts
diff --git a/experimental/traffic-portal/src/app/interceptor/alerts.interceptor.ts b/experimental/traffic-portal/src/app/shared/interceptor/alerts.interceptor.ts
similarity index 94%
rename from experimental/traffic-portal/src/app/interceptor/alerts.interceptor.ts
rename to experimental/traffic-portal/src/app/shared/interceptor/alerts.interceptor.ts
index 0687f23..af321ce 100644
--- a/experimental/traffic-portal/src/app/interceptor/alerts.interceptor.ts
+++ b/experimental/traffic-portal/src/app/shared/interceptor/alerts.interceptor.ts
@@ -16,9 +16,9 @@ import { Injectable } from "@angular/core";
 
 import { Observable } from "rxjs";
 import { tap } from "rxjs/operators";
+import {AlertService} from "../alert/alert.service";
+import {Alert} from "../../models/alert.model";
 
-import { Alert } from "../models/alert";
-import { AlertService } from "../services";
 
 /**
  * This class intercepts any and all alerts contained in API responses and
diff --git a/experimental/traffic-portal/src/app/interceptor/error.interceptor.ts b/experimental/traffic-portal/src/app/shared/interceptor/error.interceptor.ts
similarity index 87%
rename from experimental/traffic-portal/src/app/interceptor/error.interceptor.ts
rename to experimental/traffic-portal/src/app/shared/interceptor/error.interceptor.ts
index 8052791..69aa07c 100644
--- a/experimental/traffic-portal/src/app/interceptor/error.interceptor.ts
+++ b/experimental/traffic-portal/src/app/shared/interceptor/error.interceptor.ts
@@ -17,8 +17,8 @@ import { Injectable } from "@angular/core";
 import { Observable, throwError } from "rxjs";
 import { catchError } from "rxjs/operators";
 
-import { Alert } from "../models/alert";
-import { AlertService, CurrentUserService } from "../services";
+import {AlertService} from "../alert/alert.service";
+import {Alert} from "../../models/alert.model";
 
 /**
  * This class intercepts any and all HTTP error responses and checks for
@@ -28,7 +28,6 @@ import { AlertService, CurrentUserService } from "../services";
 export class ErrorInterceptor implements HttpInterceptor {
 
 	constructor(
-		private readonly currentUserService: CurrentUserService,
 		private readonly alerts: AlertService
 	) {}
 
@@ -50,9 +49,6 @@ export class ErrorInterceptor implements HttpInterceptor {
 					this.alerts.alertsSubject.next(a);
 				}
 			}
-			if (err.status === 401 || err.status === 403) {
-				this.currentUserService.logout(true);
-			}
 
 			return throwError(err);
 		}));
diff --git a/experimental/traffic-portal/src/app/components/loading/loading.component.html b/experimental/traffic-portal/src/app/shared/loading/loading.component.html
similarity index 100%
rename from experimental/traffic-portal/src/app/components/loading/loading.component.html
rename to experimental/traffic-portal/src/app/shared/loading/loading.component.html
diff --git a/experimental/traffic-portal/src/app/components/loading/loading.component.scss b/experimental/traffic-portal/src/app/shared/loading/loading.component.scss
similarity index 96%
rename from experimental/traffic-portal/src/app/components/loading/loading.component.scss
rename to experimental/traffic-portal/src/app/shared/loading/loading.component.scss
index 73ba9e8..8c8082f 100644
--- a/experimental/traffic-portal/src/app/components/loading/loading.component.scss
+++ b/experimental/traffic-portal/src/app/shared/loading/loading.component.scss
@@ -11,7 +11,7 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
-@import '../../../theme.scss';
+@import 'src/theme';
 :host {
 	flex: auto;
 	display: flex;
diff --git a/experimental/traffic-portal/src/app/components/loading/loading.component.spec.ts b/experimental/traffic-portal/src/app/shared/loading/loading.component.spec.ts
similarity index 100%
rename from experimental/traffic-portal/src/app/components/loading/loading.component.spec.ts
rename to experimental/traffic-portal/src/app/shared/loading/loading.component.spec.ts
diff --git a/experimental/traffic-portal/src/app/components/loading/loading.component.ts b/experimental/traffic-portal/src/app/shared/loading/loading.component.ts
similarity index 100%
rename from experimental/traffic-portal/src/app/components/loading/loading.component.ts
rename to experimental/traffic-portal/src/app/shared/loading/loading.component.ts
diff --git a/experimental/traffic-portal/src/app/directives/openable.directive.spec.ts b/experimental/traffic-portal/src/app/shared/openable/openable.directive.spec.ts
similarity index 100%
rename from experimental/traffic-portal/src/app/directives/openable.directive.spec.ts
rename to experimental/traffic-portal/src/app/shared/openable/openable.directive.spec.ts
diff --git a/experimental/traffic-portal/src/app/directives/openable.directive.ts b/experimental/traffic-portal/src/app/shared/openable/openable.directive.ts
similarity index 100%
rename from experimental/traffic-portal/src/app/directives/openable.directive.ts
rename to experimental/traffic-portal/src/app/shared/openable/openable.directive.ts
diff --git a/experimental/traffic-portal/src/app/shared/shared.module.ts b/experimental/traffic-portal/src/app/shared/shared.module.ts
new file mode 100644
index 0000000..38426a8
--- /dev/null
+++ b/experimental/traffic-portal/src/app/shared/shared.module.ts
@@ -0,0 +1,97 @@
+/*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+import { NgModule } from "@angular/core";
+import {HTTP_INTERCEPTORS} from "@angular/common/http";
+import {CommonModule} from "@angular/common";
+import {RouterModule} from "@angular/router";
+import {AppUIModule} from "../app.ui.module";
+import {AlertComponent} from "./alert/alert.component";
+import {ErrorInterceptor} from "./interceptor/error.interceptor";
+import {AlertInterceptor} from "./interceptor/alerts.interceptor";
+import {LoadingComponent} from "./loading/loading.component";
+import {TpHeaderComponent} from "./tp-header/tp-header.component";
+import {GenericTableComponent} from "./generic-table/generic-table.component";
+import {LinechartDirective} from "./charts/linechart.directive";
+import {AlertService} from "./alert/alert.service";
+import {
+	CacheGroupService,
+	CDNService,
+	DeliveryServiceService,
+	InvalidationJobService,
+	ProfileService,
+	ServerService, TypeService, UserService
+} from "./api";
+import {PhysicalLocationService} from "./api/PhysicalLocationService";
+import {CurrentUserService} from "./currentUser/current-user.service";
+import {CustomvalidityDirective} from "./validation/customvalidity.directive";
+import {OpenableDirective} from "./openable/openable.directive";
+import {SSHCellRendererComponent} from "./table-components/ssh-cell-renderer/ssh-cell-renderer.component";
+import {UpdateCellRendererComponent} from "./table-components/update-cell-renderer/update-cell-renderer.component";
+import {BooleanFilterComponent} from "./table-components/boolean-filter/boolean-filter.component";
+
+
+
+/**
+ * SharedModule contains common code that modules can import independently.
+ */
+@NgModule({
+	declarations: [
+		AlertComponent,
+		LoadingComponent,
+		TpHeaderComponent,
+		GenericTableComponent,
+		BooleanFilterComponent,
+		UpdateCellRendererComponent,
+
+		CustomvalidityDirective,
+		LinechartDirective,
+		OpenableDirective
+	],
+	entryComponents: [
+		SSHCellRendererComponent
+	],
+	exports: [
+		AlertComponent,
+		LoadingComponent,
+		TpHeaderComponent,
+		GenericTableComponent,
+		BooleanFilterComponent,
+		UpdateCellRendererComponent,
+
+		CustomvalidityDirective,
+		LinechartDirective,
+		OpenableDirective
+	],
+	imports: [
+		AppUIModule,
+		CommonModule,
+		RouterModule
+	],
+	providers: [
+		{multi: true, provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor},
+		{multi: true, provide: HTTP_INTERCEPTORS, useClass: AlertInterceptor},
+		AlertService,
+		CacheGroupService,
+		CDNService,
+		CurrentUserService,
+		DeliveryServiceService,
+		InvalidationJobService,
+		PhysicalLocationService,
+		ProfileService,
+		ServerService,
+		TypeService,
+		UserService
+	],
+})
+export class SharedModule { }
diff --git a/experimental/traffic-portal/src/app/components/table-components/boolean-filter/boolean-filter.component.html b/experimental/traffic-portal/src/app/shared/table-components/boolean-filter/boolean-filter.component.html
similarity index 100%
rename from experimental/traffic-portal/src/app/components/table-components/boolean-filter/boolean-filter.component.html
rename to experimental/traffic-portal/src/app/shared/table-components/boolean-filter/boolean-filter.component.html
diff --git a/experimental/traffic-portal/src/app/components/table-components/boolean-filter/boolean-filter.component.scss b/experimental/traffic-portal/src/app/shared/table-components/boolean-filter/boolean-filter.component.scss
similarity index 100%
rename from experimental/traffic-portal/src/app/components/table-components/boolean-filter/boolean-filter.component.scss
rename to experimental/traffic-portal/src/app/shared/table-components/boolean-filter/boolean-filter.component.scss
diff --git a/experimental/traffic-portal/src/app/components/table-components/boolean-filter/boolean-filter.component.spec.ts b/experimental/traffic-portal/src/app/shared/table-components/boolean-filter/boolean-filter.component.spec.ts
similarity index 100%
rename from experimental/traffic-portal/src/app/components/table-components/boolean-filter/boolean-filter.component.spec.ts
rename to experimental/traffic-portal/src/app/shared/table-components/boolean-filter/boolean-filter.component.spec.ts
diff --git a/experimental/traffic-portal/src/app/components/table-components/boolean-filter/boolean-filter.component.ts b/experimental/traffic-portal/src/app/shared/table-components/boolean-filter/boolean-filter.component.ts
similarity index 100%
rename from experimental/traffic-portal/src/app/components/table-components/boolean-filter/boolean-filter.component.ts
rename to experimental/traffic-portal/src/app/shared/table-components/boolean-filter/boolean-filter.component.ts
diff --git a/experimental/traffic-portal/src/app/components/table-components/ssh-cell-renderer/ssh-cell-renderer.component.html b/experimental/traffic-portal/src/app/shared/table-components/ssh-cell-renderer/ssh-cell-renderer.component.html
similarity index 100%
rename from experimental/traffic-portal/src/app/components/table-components/ssh-cell-renderer/ssh-cell-renderer.component.html
rename to experimental/traffic-portal/src/app/shared/table-components/ssh-cell-renderer/ssh-cell-renderer.component.html
diff --git a/experimental/traffic-portal/src/app/components/table-components/ssh-cell-renderer/ssh-cell-renderer.component.scss b/experimental/traffic-portal/src/app/shared/table-components/ssh-cell-renderer/ssh-cell-renderer.component.scss
similarity index 100%
rename from experimental/traffic-portal/src/app/components/table-components/ssh-cell-renderer/ssh-cell-renderer.component.scss
rename to experimental/traffic-portal/src/app/shared/table-components/ssh-cell-renderer/ssh-cell-renderer.component.scss
diff --git a/experimental/traffic-portal/src/app/components/table-components/ssh-cell-renderer/ssh-cell-renderer.component.spec.ts b/experimental/traffic-portal/src/app/shared/table-components/ssh-cell-renderer/ssh-cell-renderer.component.spec.ts
similarity index 76%
rename from experimental/traffic-portal/src/app/components/table-components/ssh-cell-renderer/ssh-cell-renderer.component.spec.ts
rename to experimental/traffic-portal/src/app/shared/table-components/ssh-cell-renderer/ssh-cell-renderer.component.spec.ts
index 40d8e48..7411966 100644
--- a/experimental/traffic-portal/src/app/components/table-components/ssh-cell-renderer/ssh-cell-renderer.component.spec.ts
+++ b/experimental/traffic-portal/src/app/shared/table-components/ssh-cell-renderer/ssh-cell-renderer.component.spec.ts
@@ -15,6 +15,7 @@ import { HttpClientModule } from "@angular/common/http";
 import { waitForAsync, ComponentFixture, TestBed } from "@angular/core/testing";
 import { RouterTestingModule } from "@angular/router/testing";
 
+import { CurrentUserService } from "src/app/shared/currentUser/current-user.service";
 import { SSHCellRendererComponent } from "./ssh-cell-renderer.component";
 
 describe("SshCellRendererComponent", () => {
@@ -22,9 +23,12 @@ describe("SshCellRendererComponent", () => {
 	let fixture: ComponentFixture<SSHCellRendererComponent>;
 
 	beforeEach(waitForAsync(() => {
+		const mockCurrentUserService = jasmine.createSpyObj(["updateCurrentUser", "login", "logout"]);
+		mockCurrentUserService.updateCurrentUser.and.returnValue(new Promise(r => r(false)));
 		TestBed.configureTestingModule({
 			declarations: [ SSHCellRendererComponent ],
-			imports: [HttpClientModule, RouterTestingModule]
+			imports: [HttpClientModule, RouterTestingModule],
+			providers: [ { provide: CurrentUserService, useValue: mockCurrentUserService} ]
 		})
 			.compileComponents();
 	}));
diff --git a/experimental/traffic-portal/src/app/components/table-components/ssh-cell-renderer/ssh-cell-renderer.component.ts b/experimental/traffic-portal/src/app/shared/table-components/ssh-cell-renderer/ssh-cell-renderer.component.ts
similarity index 93%
rename from experimental/traffic-portal/src/app/components/table-components/ssh-cell-renderer/ssh-cell-renderer.component.ts
rename to experimental/traffic-portal/src/app/shared/table-components/ssh-cell-renderer/ssh-cell-renderer.component.ts
index bcbe59d..63f1bbc 100644
--- a/experimental/traffic-portal/src/app/components/table-components/ssh-cell-renderer/ssh-cell-renderer.component.ts
+++ b/experimental/traffic-portal/src/app/shared/table-components/ssh-cell-renderer/ssh-cell-renderer.component.ts
@@ -17,8 +17,8 @@ import { DomSanitizer, SafeUrl } from "@angular/platform-browser";
 
 import { ICellRendererAngularComp } from "ag-grid-angular";
 import { ICellRendererParams } from "ag-grid-community";
+import {CurrentUserService} from "src/app/shared/currentUser/current-user.service";
 
-import { AuthenticationService } from "../../../services";
 
 /**
  * SSHCellRendererComponent is an AG-Grid cell renderer that provides ssh:// links as content.
@@ -50,7 +50,7 @@ export class SSHCellRendererComponent implements ICellRendererAngularComp {
 	/**
 	 * Constructor.
 	 */
-	constructor(private readonly auth: AuthenticationService, private readonly sanitizer: DomSanitizer) {
+	constructor(private readonly auth: CurrentUserService, private readonly sanitizer: DomSanitizer) {
 		this.auth.updateCurrentUser().then(
 			success => {
 				if (success) {
diff --git a/experimental/traffic-portal/src/app/components/table-components/update-cell-renderer/update-cell-renderer.component.html b/experimental/traffic-portal/src/app/shared/table-components/update-cell-renderer/update-cell-renderer.component.html
similarity index 100%
rename from experimental/traffic-portal/src/app/components/table-components/update-cell-renderer/update-cell-renderer.component.html
rename to experimental/traffic-portal/src/app/shared/table-components/update-cell-renderer/update-cell-renderer.component.html
diff --git a/experimental/traffic-portal/src/app/components/table-components/update-cell-renderer/update-cell-renderer.component.scss b/experimental/traffic-portal/src/app/shared/table-components/update-cell-renderer/update-cell-renderer.component.scss
similarity index 94%
rename from experimental/traffic-portal/src/app/components/table-components/update-cell-renderer/update-cell-renderer.component.scss
rename to experimental/traffic-portal/src/app/shared/table-components/update-cell-renderer/update-cell-renderer.component.scss
index 1856d37..ad4111c 100644
--- a/experimental/traffic-portal/src/app/components/table-components/update-cell-renderer/update-cell-renderer.component.scss
+++ b/experimental/traffic-portal/src/app/shared/table-components/update-cell-renderer/update-cell-renderer.component.scss
@@ -12,7 +12,7 @@
 * limitations under the License.
 */
 
-@import "../../../../theme.scss";
+@import "src/theme";
 
 fa-icon {
 	color: darkgray;
diff --git a/experimental/traffic-portal/src/app/components/table-components/update-cell-renderer/update-cell-renderer.component.spec.ts b/experimental/traffic-portal/src/app/shared/table-components/update-cell-renderer/update-cell-renderer.component.spec.ts
similarity index 100%
rename from experimental/traffic-portal/src/app/components/table-components/update-cell-renderer/update-cell-renderer.component.spec.ts
rename to experimental/traffic-portal/src/app/shared/table-components/update-cell-renderer/update-cell-renderer.component.spec.ts
diff --git a/experimental/traffic-portal/src/app/components/table-components/update-cell-renderer/update-cell-renderer.component.ts b/experimental/traffic-portal/src/app/shared/table-components/update-cell-renderer/update-cell-renderer.component.ts
similarity index 100%
rename from experimental/traffic-portal/src/app/components/table-components/update-cell-renderer/update-cell-renderer.component.ts
rename to experimental/traffic-portal/src/app/shared/table-components/update-cell-renderer/update-cell-renderer.component.ts
diff --git a/experimental/traffic-portal/src/app/components/tp-header/tp-header.component.html b/experimental/traffic-portal/src/app/shared/tp-header/tp-header.component.html
similarity index 54%
rename from experimental/traffic-portal/src/app/components/tp-header/tp-header.component.html
rename to experimental/traffic-portal/src/app/shared/tp-header/tp-header.component.html
index 47094ac..739ff41 100644
--- a/experimental/traffic-portal/src/app/components/tp-header/tp-header.component.html
+++ b/experimental/traffic-portal/src/app/shared/tp-header/tp-header.component.html
@@ -17,23 +17,23 @@ limitations under the License.
 	<div></div>
 	<nav id="expanded">
 		<ul>
-			<li><a routerLink="/">Home</a></li>
-			<li *ngIf="permissions.has('users-read')"><a routerLink="/users">Users</a></li>
-			<li *ngIf="permissions.has('servers-read')"><a routerLink="/servers">Servers</a></li>
+			<li><a routerLink="/core/">Home</a></li>
+			<li ><a routerLink="/core/users">Users</a></li>
+			<li ><a routerLink="/core/servers">Servers</a></li>
 			<li><button class="menubutton" type="button" (click)="logout()">Logout</button></li>
-			<li><a href="/me"><svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 16 16"><g id="surface1"><path d="M 8 2 C 6.347656 2 5 3.347656 5 5 C 5 6.652344 6.347656 8 8 8 C 9.652344 8 11 6.652344 11 5 C 11 3.347656 9.652344 2 8 2 Z M 8 8 C 5.246094 8 3 10.246094 3 13 L 4 13 C 4 10.785156 5.785156 9 8 9 C 10.214844 9 12 10.785156 12 13 L 13 13 C 13 10.246094 10.753906 8 8 8 Z M 8 3 C 9.109375 3 10 3.890625 10 5 C 10 6.109375 9.109375 7 8 7 C 6.890625 7 6 6.109375 6 5 C 6 3.8 [...]
-</a></li>
+			<li><a routerLink="/core/me"><svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 16 16"><g id="surface1"><path d="M 8 2 C 6.347656 2 5 3.347656 5 5 C 5 6.652344 6.347656 8 8 8 C 9.652344 8 11 6.652344 11 5 C 11 3.347656 9.652344 2 8 2 Z M 8 8 C 5.246094 8 3 10.246094 3 13 L 4 13 C 4 10.785156 5.785156 9 8 9 C 10.214844 9 12 10.785156 12 13 L 13 13 C 13 10.246094 10.753906 8 8 8 Z M 8 3 C 9.109375 3 10 3.890625 10 5 C 10 6.109375 9.109375 7 8 7 C 6.890625 7 6 6.109375  [...]
+			</a></li>
 		</ul>
 	</nav>
 	<nav id="collapsed">
 		<input type="checkbox" hidden id="collapsed-menu"/>
 		<label for="collapsed-menu"></label>
 		<ul>
-			<li><a routerLink="/">Home</a></li>
-			<li><a routerLink="/users">Users</a></li>
-			<li><a routerLink="/servers">Servers</a></li>
+			<li><a routerLink="/core/">Home</a></li>
+			<li><a routerLink="/core/users">Users</a></li>
+			<li><a routerLink="/core/servers">Servers</a></li>
 			<li><a href="#">Tools</a></li>
-			<li><a href="/me">Profile</a></li>
+			<li><a href="/core/me">Profile</a></li>
 			<li><button type="button" class="menubutton" (click)="logout()">Logout</button></li>
 		</ul>
 	</nav>
diff --git a/experimental/traffic-portal/src/app/components/tp-header/tp-header.component.scss b/experimental/traffic-portal/src/app/shared/tp-header/tp-header.component.scss
similarity index 98%
rename from experimental/traffic-portal/src/app/components/tp-header/tp-header.component.scss
rename to experimental/traffic-portal/src/app/shared/tp-header/tp-header.component.scss
index 5d0c02e..f0504be 100644
--- a/experimental/traffic-portal/src/app/components/tp-header/tp-header.component.scss
+++ b/experimental/traffic-portal/src/app/shared/tp-header/tp-header.component.scss
@@ -11,7 +11,7 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
-@import '../../../theme.scss';
+@import 'src/theme';
 mat-toolbar {
 	display: inline-flex;
 	width: 100%;
diff --git a/experimental/traffic-portal/src/app/components/tp-header/tp-header.component.spec.ts b/experimental/traffic-portal/src/app/shared/tp-header/tp-header.component.spec.ts
similarity index 75%
rename from experimental/traffic-portal/src/app/components/tp-header/tp-header.component.spec.ts
rename to experimental/traffic-portal/src/app/shared/tp-header/tp-header.component.spec.ts
index 45c4f4f..afdf334 100644
--- a/experimental/traffic-portal/src/app/components/tp-header/tp-header.component.spec.ts
+++ b/experimental/traffic-portal/src/app/shared/tp-header/tp-header.component.spec.ts
@@ -15,6 +15,8 @@ import { HttpClientModule } from "@angular/common/http";
 import { waitForAsync, ComponentFixture, TestBed } from "@angular/core/testing";
 import { RouterTestingModule } from "@angular/router/testing";
 
+import { CurrentUserService } from "src/app/shared/currentUser/current-user.service";
+import {UserService} from "../api";
 import { TpHeaderComponent } from "./tp-header.component";
 
 describe("TpHeaderComponent", () => {
@@ -22,9 +24,15 @@ describe("TpHeaderComponent", () => {
 	let fixture: ComponentFixture<TpHeaderComponent>;
 
 	beforeEach(waitForAsync(() => {
+		const mockAPIService = jasmine.createSpyObj(["getUsers"]);
+		const mockCurrentUserService = jasmine.createSpyObj(["updateCurrentUser", "login", "logout"]);
 		TestBed.configureTestingModule({
 			declarations: [ TpHeaderComponent ],
-			imports: [ HttpClientModule, RouterTestingModule ]
+			imports: [ HttpClientModule, RouterTestingModule ],
+			providers: [
+				{ provide: CurrentUserService, useValue: mockCurrentUserService },
+				{ provide: UserService, useValue: mockAPIService}
+			]
 		})
 			.compileComponents();
 	}));
diff --git a/experimental/traffic-portal/src/app/components/tp-header/tp-header.component.ts b/experimental/traffic-portal/src/app/shared/tp-header/tp-header.component.ts
similarity index 88%
rename from experimental/traffic-portal/src/app/components/tp-header/tp-header.component.ts
rename to experimental/traffic-portal/src/app/shared/tp-header/tp-header.component.ts
index ab185ff..7ccece7 100644
--- a/experimental/traffic-portal/src/app/components/tp-header/tp-header.component.ts
+++ b/experimental/traffic-portal/src/app/shared/tp-header/tp-header.component.ts
@@ -12,8 +12,8 @@
 * limitations under the License.
 */
 import { Component, Input, OnInit } from "@angular/core";
-import { AuthenticationService } from "src/app/services";
-import { UserService } from "src/app/services/api";
+import { UserService } from "src/app/shared/api";
+import {CurrentUserService} from "src/app/shared/currentUser/current-user.service";
 
 /**
  * TpHeaderComponent is the controller for the standard Traffic Portal header.
@@ -43,7 +43,7 @@ export class TpHeaderComponent implements OnInit {
 	@Input() public title?: string;
 
 	/** Constructor */
-	constructor(private readonly auth: AuthenticationService, private readonly api: UserService) {
+	constructor(private readonly auth: CurrentUserService, private readonly api: UserService) {
 	}
 
 	/** Sets up data dependencies. */
diff --git a/experimental/traffic-portal/src/app/directives/customvalidity.directive.spec.ts b/experimental/traffic-portal/src/app/shared/validation/customvalidity.directive.spec.ts
similarity index 100%
rename from experimental/traffic-portal/src/app/directives/customvalidity.directive.spec.ts
rename to experimental/traffic-portal/src/app/shared/validation/customvalidity.directive.spec.ts
diff --git a/experimental/traffic-portal/src/app/directives/customvalidity.directive.ts b/experimental/traffic-portal/src/app/shared/validation/customvalidity.directive.ts
similarity index 100%
rename from experimental/traffic-portal/src/app/directives/customvalidity.directive.ts
rename to experimental/traffic-portal/src/app/shared/validation/customvalidity.directive.ts