├── LICENSE ├── README.md ├── css └── bike.css ├── html ├── admin.component.html ├── home.component.html └── view-registration.component.html ├── java └── SecurityConfiguration.java ├── js └── auth.service.ts ├── node ├── README.md └── server.js ├── proxy └── proxy.conf.json └── sql ├── bike.db └── bike.sql /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Dan Bunker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ps-spring-boot-and-angular -------------------------------------------------------------------------------- /css/bike.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | } 4 | 5 | body { 6 | padding-top: 10px; 7 | padding-bottom: 10px; 8 | text-rendering: optimizeLegibility; 9 | position: relative; 10 | min-height: 100%; 11 | -webkit-font-smoothing: antialiased; 12 | -moz-osx-font-smoothing: grayscale; 13 | font-family: 'Courier New', sans-serif; 14 | font-weight: normal; 15 | font-size: 1.5em; 16 | background: linear-gradient(150deg,#D5E3F1 15%,#FFFBD9 70%,#FFDFCB 94%); 17 | } 18 | 19 | h1 { 20 | font-size: 2.0em; 21 | } 22 | 23 | .table-bordered, .table-bordered td, .table-bordered th { 24 | border: 1px solid #2F465D; 25 | } 26 | -------------------------------------------------------------------------------- /html/admin.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 16 | 19 | 22 | 23 |
Owner NameEmailModelPurchase Price
11 | {{bike.name}} 12 | 14 | {{bike.email}} 15 | 17 | {{bike.model}} 18 | 20 | {{bike.purchasePrice}} 21 |
24 |
25 | -------------------------------------------------------------------------------- /html/home.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

{{validMessage}}

5 |
6 |
7 | 8 | 9 |
10 |
11 | 12 | 13 |
14 |
15 | 16 | 17 |
18 |
19 | 20 | 27 |
28 |
29 | 30 | 31 |
32 |
33 | 34 | 35 |
36 |
37 | 38 | 39 |
40 | 41 | 42 |
43 |
44 | -------------------------------------------------------------------------------- /html/view-registration.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | Back to List 4 |
5 |
6 |
7 | 8 |
{{bikeReg.name}}
9 |
10 |
11 | 12 |
{{bikeReg.email}}
13 |
14 |
15 | 16 |
{{bikeReg.phone}}
17 |
18 |
19 | 20 |
{{bikeReg.model}}
21 |
22 |
23 | 24 |
{{bikeReg.serialNumber}}
25 |
26 |
27 | 28 |
{{bikeReg.purchasePrice}}
29 |
30 |
31 | 32 |
{{bikeReg.purchaseDate}}
33 |
34 |
35 |
36 | -------------------------------------------------------------------------------- /java/SecurityConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.globomatics.bike.config; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.http.HttpMethod; 6 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 7 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 8 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 9 | 10 | import com.auth0.spring.security.api.JwtWebSecurityConfigurer; 11 | 12 | @EnableWebSecurity 13 | @Configuration 14 | public class SecurityConfiguration extends WebSecurityConfigurerAdapter { 15 | @Value(value = "${auth0.apiAudience}") 16 | private String apiAudience; 17 | @Value(value = "${auth0.issuer}") 18 | private String issuer; 19 | 20 | @Override 21 | protected void configure(HttpSecurity http) throws Exception { 22 | JwtWebSecurityConfigurer 23 | .forRS256(apiAudience, issuer) 24 | .configure(http) 25 | .authorizeRequests() 26 | .antMatchers(HttpMethod.POST, "/api/v1/bikes").permitAll() 27 | .antMatchers(HttpMethod.GET, "/api/v1/bikes").hasAuthority("view:registrations") 28 | .antMatchers(HttpMethod.GET, "/api/v1/bikes/**").hasAuthority("view:registration") 29 | .anyRequest().authenticated(); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /js/auth.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Router } from '@angular/router'; 3 | import 'rxjs/add/operator/filter'; 4 | import * as auth0 from 'auth0-js'; 5 | 6 | @Injectable() 7 | export class AuthService { 8 | 9 | auth0 = new auth0.WebAuth({ 10 | clientID: '', 11 | domain: '', 12 | responseType: 'token id_token', 13 | audience: 'http://localhost:8080', 14 | redirectUri: 'http://localhost:4200/callback', 15 | scope: 'openid view:registration view:registrations' 16 | }); 17 | 18 | constructor(public router: Router) {} 19 | 20 | public login(): void { 21 | this.auth0.authorize(); 22 | } 23 | 24 | public handleAuthentication(): void { 25 | this.auth0.parseHash((err, authResult) => { 26 | if (authResult && authResult.accessToken && authResult.idToken) { 27 | window.location.hash = ''; 28 | this.setSession(authResult); 29 | this.router.navigate(['/admin']); 30 | } else if (err) { 31 | this.router.navigate(['/admin']); 32 | console.log(err); 33 | } 34 | }); 35 | } 36 | 37 | private setSession(authResult): void { 38 | // Set the time that the access token will expire at 39 | const expiresAt = JSON.stringify((authResult.expiresIn * 1000) + new Date().getTime()); 40 | localStorage.setItem('access_token', authResult.accessToken); 41 | localStorage.setItem('id_token', authResult.idToken); 42 | localStorage.setItem('expires_at', expiresAt); 43 | } 44 | 45 | public logout(): void { 46 | // Remove tokens and expiry time from localStorage 47 | localStorage.removeItem('access_token'); 48 | localStorage.removeItem('id_token'); 49 | localStorage.removeItem('expires_at'); 50 | // Go back to the home route 51 | this.router.navigate(['/']); 52 | } 53 | 54 | public isAuthenticated(): boolean { 55 | // Check whether the current time is past the 56 | // access token's expiry time 57 | const expiresAt = JSON.parse(localStorage.getItem('expires_at')); 58 | return new Date().getTime() < expiresAt; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /node/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dlbunker/ps-spring-boot-and-angular/3ab9d01470a2a97a6920465efb575c2e0d02935c/node/README.md -------------------------------------------------------------------------------- /node/server.js: -------------------------------------------------------------------------------- 1 | // Get dependencies 2 | const express = require('express'); 3 | const path = require('path'); 4 | const http = require('http'); 5 | const bodyParser = require('body-parser'); 6 | const proxy = require('express-http-proxy'); 7 | const cors = require('cors'); 8 | 9 | const app = express(); 10 | 11 | // Parsers for POST data 12 | app.use(bodyParser.json({limit: '20mb'})); 13 | app.use(bodyParser.urlencoded({ extended: false, limit: '20mb' })); 14 | 15 | app.use(cors()); 16 | 17 | // Point static path to dist 18 | app.use(express.static(path.join(__dirname, 'dist'))); 19 | 20 | // Set our api routes proxy to point to spring boot server 21 | app.use('/server', proxy('http://localhost:8080')); 22 | 23 | // Catch all other routes and return the index file 24 | app.get('*', (req, res) => { 25 | res.sendFile(path.join(__dirname, 'dist/index.html')); 26 | }); 27 | 28 | /** 29 | * Get port from environment and store in Express. 30 | */ 31 | const port = '4200'; 32 | app.set('port', port); 33 | 34 | /** 35 | * Create HTTP server. 36 | */ 37 | const server = http.createServer(app); 38 | 39 | /** 40 | * Listen on provided port, on all network interfaces. 41 | */ 42 | server.listen(port, () => console.log(`API running on ${port}`)); 43 | -------------------------------------------------------------------------------- /proxy/proxy.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "/server": { 3 | "target": "http://localhost:8080", 4 | "secure": false, 5 | "changeOrigin": true, 6 | "logLevel": "debug", 7 | "pathRewrite": { 8 | "^/server" : "" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /sql/bike.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dlbunker/ps-spring-boot-and-angular/3ab9d01470a2a97a6920465efb575c2e0d02935c/sql/bike.db -------------------------------------------------------------------------------- /sql/bike.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE 2 | bike 3 | ( 4 | id BIGINT NOT NULL, 5 | contact BOOLEAN NOT NULL, 6 | email VARCHAR, 7 | model VARCHAR, 8 | name VARCHAR, 9 | phone VARCHAR, 10 | purchase_date DATETIME, 11 | purchase_price NUMERIC, 12 | serial_number VARCHAR, 13 | PRIMARY KEY (id) 14 | ); 15 | 16 | CREATE TABLE 17 | hibernate_sequence 18 | ( 19 | next_val BIGINT 20 | ); 21 | 22 | INSERT INTO bike (id, contact, email, model, name, phone, purchase_date, purchase_price) 23 | VALUES (1, 1, 'jeff@bikes.com', 'Globo MTB 29 Full Suspension', 'Jeff Miller', '328-443-5555', 4419619200000, '1100'); 24 | INSERT INTO bike (id, contact, email, model, name, phone, purchase_date, purchase_price) 25 | VALUES (2, 0, 'samantha@bikes.com', 'Globo Carbon Fiber Race Series', 'Samantha Davis', '448-397-5555', 4419619200000, '1999'); 26 | INSERT INTO bike (id, contact, email, model, name, phone, purchase_date, purchase_price) 27 | VALUES (3, 1, 'dave@bikes.com', 'Globo Time Trial Blade', 'Dave Warren', '563-891-5555', 4419619200000, '2100'); 28 | 29 | INSERT INTO hibernate_sequence (next_val) VALUES (4); 30 | --------------------------------------------------------------------------------