Rename and fixes

This commit is contained in:
Karolis Kundrotas
2021-09-10 11:59:34 +03:00
parent 257308e096
commit c3bb8983ef
29 changed files with 11 additions and 27 deletions

23
KTUSAPS/ClientApp/.gitignore vendored Normal file
View File

@@ -0,0 +1,23 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View File

@@ -0,0 +1,6 @@
module.exports = {
trailingComma: 'es5',
tabWidth: 2,
semi: false,
singleQuote: true,
}

View File

@@ -0,0 +1,24 @@
# clientapp
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

View File

@@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

12269
KTUSAPS/ClientApp/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,52 @@
{
"name": "clientapp",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"@popperjs/core": "^2.10.1",
"axios": "^0.20.0-0",
"bootstrap": "^5.1.1",
"cookies-js": "^1.2.3",
"core-js": "^3.7.0",
"jwt-decode": "^3.1.2",
"vue": "^3.0.2",
"vue-loader-v16": "npm:vue-loader@^16.0.0-alpha.3",
"vue-router": "^4.0.0-rc.5",
"vuex": "^4.0.2"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.5.9",
"@vue/cli-plugin-eslint": "^4.5.9",
"@vue/cli-service": "^4.5.9",
"@vue/compiler-sfc": "^3.0.2",
"babel-eslint": "^10.1.0",
"eslint": "^6.8.0",
"eslint-plugin-vue": "^7.1.0",
"sass": "^1.33.0",
"sass-loader": "^10.2.0"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/vue3-essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

View File

@@ -0,0 +1,49 @@
<template>
<nav-menu></nav-menu>
<div v-if="isLocal" class="container">
<div class="alert alert-danger">
<h4 class="alert-heading">Lokali sistemą aptikta</h4>
<span>
Buvo aptikta, kad aplikacija veikia ant lokalios mašinos. Tai didelis
šansas kad sistemoje pateikiama informacija nėra saugi.
</span>
</div>
</div>
<div v-if="isInsecure" class="container">
<div class="alert alert-danger">
<h4 class="alert-heading">Nesaugus ryšys</h4>
<span>
Buvo aptikta, kad yra užmegztas nesaugus ryšys. Tai reiškią kad betkokie
duomenys perduodami naudojant sistemą, įskaitant ir prisijungimo tokeną,
yra neapsaugoti.
</span>
</div>
</div>
<router-view />
</template>
<script>
import NavMenu from './components/NavMenu.vue'
export default {
name: 'App',
components: {
NavMenu,
},
created() {
this.$store.dispatch('auth/initialize')
},
computed: {
isLocal() {
return (
location.hostname === 'localhost' || location.hostname.startsWith('127')
)
},
isInsecure() {
return location.protocol !== 'https:'
},
},
}
</script>
<style></style>

View File

@@ -0,0 +1 @@


Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -0,0 +1,2 @@
@import "custom";
@import "~bootstrap/scss/bootstrap";

View File

@@ -0,0 +1,65 @@
<template>
<header class="mb-3">
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container-fluid">
<router-link :to="{ name: 'Home' }" class="navbar-brand"
>KTU SA Problemų sprendimo sistema</router-link
>
<button
class="navbar-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarNav"
aria-controls="navbarNav"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span class="navbar-toggler-icon"></span>
</button>
<div
class="collapse navbar-collapse"
:class="{ show: isExpanded }"
id="navbarNav"
>
<ul class="navbar-nav me-auto">
<li class="nav-item">
<router-link :to="{ name: 'Home' }" class="nav-link"
>Pagrindinis</router-link
>
</li>
</ul>
<div class="navbar-nav">
<span v-if="$store.getters['auth/isValid']" class="navbar-text"
>Prisijungta kaip {{ $store.getters['auth/email'] }}</span
>
<div v-else class="nav-item">
<a :href="$store.getters['auth/loginUrl']" class="nav-link"
>Prisijungti</a
>
</div>
</div>
</div>
</div>
</nav>
</header>
</template>
<script>
export default {
name: 'NavMenu',
data() {
return {
isExpanded: false,
}
},
methods: {
collapse() {
this.isExpanded = false
},
toggle() {
this.isExpanded = !this.isExpanded
},
},
}
</script>

View File

@@ -0,0 +1,15 @@
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import './assets/main.scss'
import 'bootstrap'
const app = createApp(App)
app.use(router)
app.use(store)
app.mount('#app')
window.r = router

View File

@@ -0,0 +1,107 @@
<template>
<div class="container">
<h1>KTU SA Problemų sprendimo sistema</h1>
<template v-if="$store.getters['auth/isValid']">
<div class="alert alert-success">
<h4 class="alert-heading">Tu esi prisijungęs</h4>
<span>
Kliento aplikacija turi tavo saugos raktą. Aplikacija žino, kad tavo
el. paštas yra: <b>{{ $store.getters['auth/email'] }}</b>
</span>
</div>
<h2>Visi laukai gaunami Azure Active Directory</h2>
<table class="table">
<thead>
<tr>
<th scope="col">Pavadinimas</th>
<th scope="col">Reikšmė</th>
</tr>
</thead>
<tbody>
<tr v-for="(value, key) in authDataTable" :key="key">
<td>{{ key }}</td>
<td>
<pre>{{ value }}</pre>
</td>
</tr>
</tbody>
</table>
<h3>Techninė duomenų reprezentacija</h3>
<a
href="https://docs.microsoft.com/en-us/azure/active-directory/develop/access-tokens"
>Dokumentacija apie laukų reikšmes</a
>
<pre>{{ $store.state.auth.tokenData }}</pre>
<h3>Saugos raktas.</h3>
<pre>{{ $store.state.auth.token }}</pre>
<h3>Serverio tokeno patikrinimas</h3>
<button type="button" class="btn btn-primary" @click="serverVerify">
Patikrinti
</button>
<h5>Verifikacijos atsakas:</h5>
<pre>
{{ verificationResult }}
</pre>
</template>
<div v-else class="alert alert-danger" role="alert">
<h4 class="alert-heading">Tu neprisijungęs</h4>
<p>Prašom paspausti prisijungimo mygtuką navigacijos juostoje.</p>
</div>
</div>
</template>
<script>
import axios from 'axios'
const names = {
aud: 'AppId (Audience)',
iss: 'Išdavėjas',
iat: 'Išdavimo momentas',
nbf: 'Negalioja anksčiau nei',
exp: 'Galiojimo pabaiga',
email: 'El. paštas',
nonce: 'Aplikacijos sugeneruota nepasikartojanti reikšmė',
sub: 'Subjektas (Vartotojo Id)',
tid: 'Tenanto Identifikatorius',
ver: 'OAuth versija',
}
function lookupName(key) {
if (names[key]) return names[key]
return key
}
export default {
data() {
return {
verificationResult: null,
}
},
computed: {
authDataTable() {
return Object.fromEntries(
Object.entries(this.$store.state.auth.tokenData).map(([key, value]) => [
lookupName(key),
value,
])
)
},
},
methods: {
serverVerify() {
this.verificationResult = null
axios
.get('/test/authed', {
headers: { Authorization: `Bearer ${this.$store.state.auth.token}` },
})
.then(response => {
this.verificationResult = response.data
})
.catch(function(error) {
alert(error)
})
},
},
}
</script>

View File

@@ -0,0 +1,25 @@
<template>
<div class="container">
<h1>Palaukite kol nustatysime jūsų tapatybe...</h1>
</div>
</template>
<script>
const tokenRegex = /id_token=(.*\..*\..*)&/
export default {
name: 'OIDC',
created() {
if (this.openIdToken)
this.$store.dispatch('auth/setToken', this.openIdToken)
this.$router.push({ name: 'Home' })
},
computed: {
openIdToken() {
const matches = this.$route.hash.match(tokenRegex)
if (!matches) return null
return matches[1]
},
},
}
</script>

View File

@@ -0,0 +1,23 @@
import { createWebHistory, createRouter } from 'vue-router'
import Home from '@/pages/Home.vue'
import OidcEndpoint from '@/pages/OidcEndpoint.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home,
},
{
path: '/oidc',
name: 'OpenID connect endpoint',
component: OidcEndpoint,
},
]
const router = createRouter({
history: createWebHistory(),
routes,
})
export default router

View File

@@ -0,0 +1,12 @@
import { createStore, createLogger } from "vuex";
import auth from "./modules/auth";
const debug = process.env.NODE_ENV !== "production";
export default createStore({
modules: {
auth,
},
strict: debug,
plugins: debug ? [createLogger()] : [],
});

View File

@@ -0,0 +1,145 @@
import Cookies from 'cookies-js'
import jwt_decode from 'jwt-decode'
import axios from 'axios'
const TokenCookieName = 'ktusaktutoken'
const ClientIdCookieName = 'ktusakacas'
const AuthorityCookieName = 'ktusakeksas'
const TenantCookieName = 'ktusalaimis'
const NonceCookieName = 'ktusakumpikas'
const Scope = 'openid email'
// initial state
const state = () => ({
token: null,
tokenData: null,
clientId: null, // 5931fda0-e9e0-4754-80c2-18bcb9d9561a
authority: null, // https://login.microsoftonline.com/3415f2f7-f5a8-4092-b52a-003aaf844853/v2.0
tenant: null, // 3415f2f7-f5a8-4092-b52a-003aaf844853
})
const callbackUrl =
window.location.protocol + '//' + window.location.host + '/oidc'
// getters
const getters = {
isReady(state) {
if (
state.clientId == null ||
state.authority == null ||
state.tenant == null
)
return false
return true
},
isValid(state, getters) {
if (!getters.isReady) return false
if (state.token == null || state.tokenData == null) return false
const d = state.tokenData
if (d.nonce !== state.nonce) return false
if (d.iss !== state.authority) return false
if (d.aud !== state.clientId) return false
const now = new Date()
const exp = new Date(d.exp * 1000)
if (now > exp) return false
return true
},
email(state, getters) {
if (!getters.isValid) return null
return state.tokenData.email
},
userId(state, getters) {
if (!getters.isValid) return null
return state.tokenData.email
},
loginUrl(state, getters) {
if (!getters.isReady) return null
return `https://login.microsoftonline.com/${
state.tenant
}/oauth2/v2.0/authorize?client_id=${
state.clientId
}&redirect_uri=${encodeURIComponent(
callbackUrl
)}&response_type=id_token&scope=${Scope}&nonce=${state.nonce}`
},
}
// actions
const actions = {
async initialize({ commit }) {
const token = Cookies.get(TokenCookieName)
const primaryClientId = Cookies.get(ClientIdCookieName)
const primaryAuthority = Cookies.get(AuthorityCookieName)
const primaryTenant = Cookies.get(TenantCookieName)
const nonce = Cookies.get(NonceCookieName)
if (!nonce) {
const newNonce =
Date.now().toString(36) +
Math.random()
.toString(36)
.substring(2)
Cookies.set(NonceCookieName, newNonce)
commit('setNonce', newNonce)
} else {
commit('setNonce', nonce)
}
commit('setToken', token)
commit('computeTokenVars')
commit('setMetadata', [primaryClientId, primaryAuthority, primaryTenant])
axios
.get('/api/AuthMetadata')
.then(response => {
Cookies.set(ClientIdCookieName, response.data.clientId)
Cookies.set(AuthorityCookieName, response.data.authority)
Cookies.set(TenantCookieName, response.data.tenant)
commit('setMetadata', [
response.data.clientId,
response.data.authority,
response.data.tenant,
])
})
.catch(error => {
console.error(error)
})
},
async setToken({ commit }, token) {
Cookies.set(TokenCookieName, token)
commit('setToken', token)
commit('computeTokenVars')
},
}
// mutations
const mutations = {
setToken(state, token) {
state.token = token
},
setNonce(state, nonce) {
state.nonce = nonce
},
computeTokenVars(state) {
if (state.token == null) return
try {
state.tokenData = jwt_decode(state.token)
} catch {
console.log('Token was invalid.')
state.tokenData = null
state.token = null
}
},
setMetadata(state, [clientId, authority, tenant]) {
state.clientId = clientId
state.authority = authority
state.tenant = tenant
},
}
export default {
namespaced: true,
state,
getters,
actions,
mutations,
}

View File

@@ -0,0 +1,8 @@
module.exports = {
runtimeCompiler: true,
chainWebpack: (config) => {
config.resolve.alias
.set("balm-ui-plus", "balm-ui/dist/balm-ui-plus.js")
.set("balm-ui-css", "balm-ui/dist/balm-ui.css");
},
};