Rename and fixes
This commit is contained in:
23
KTUSAPS/ClientApp/.gitignore
vendored
Normal file
23
KTUSAPS/ClientApp/.gitignore
vendored
Normal 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?
|
6
KTUSAPS/ClientApp/.prettierrc.js
Normal file
6
KTUSAPS/ClientApp/.prettierrc.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
trailingComma: 'es5',
|
||||
tabWidth: 2,
|
||||
semi: false,
|
||||
singleQuote: true,
|
||||
}
|
24
KTUSAPS/ClientApp/README.md
Normal file
24
KTUSAPS/ClientApp/README.md
Normal 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/).
|
5
KTUSAPS/ClientApp/babel.config.js
Normal file
5
KTUSAPS/ClientApp/babel.config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
12269
KTUSAPS/ClientApp/package-lock.json
generated
Normal file
12269
KTUSAPS/ClientApp/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
52
KTUSAPS/ClientApp/package.json
Normal file
52
KTUSAPS/ClientApp/package.json
Normal 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"
|
||||
]
|
||||
}
|
BIN
KTUSAPS/ClientApp/public/favicon.ico
Normal file
BIN
KTUSAPS/ClientApp/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
17
KTUSAPS/ClientApp/public/index.html
Normal file
17
KTUSAPS/ClientApp/public/index.html
Normal 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>
|
49
KTUSAPS/ClientApp/src/App.vue
Normal file
49
KTUSAPS/ClientApp/src/App.vue
Normal 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>
|
1
KTUSAPS/ClientApp/src/assets/_custom.scss
Normal file
1
KTUSAPS/ClientApp/src/assets/_custom.scss
Normal file
@@ -0,0 +1 @@
|
||||
|
BIN
KTUSAPS/ClientApp/src/assets/logo.png
Normal file
BIN
KTUSAPS/ClientApp/src/assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.7 KiB |
2
KTUSAPS/ClientApp/src/assets/main.scss
Normal file
2
KTUSAPS/ClientApp/src/assets/main.scss
Normal file
@@ -0,0 +1,2 @@
|
||||
@import "custom";
|
||||
@import "~bootstrap/scss/bootstrap";
|
65
KTUSAPS/ClientApp/src/components/NavMenu.vue
Normal file
65
KTUSAPS/ClientApp/src/components/NavMenu.vue
Normal 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>
|
15
KTUSAPS/ClientApp/src/main.js
Normal file
15
KTUSAPS/ClientApp/src/main.js
Normal 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
|
107
KTUSAPS/ClientApp/src/pages/Home.vue
Normal file
107
KTUSAPS/ClientApp/src/pages/Home.vue
Normal 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 iš 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>
|
25
KTUSAPS/ClientApp/src/pages/OidcEndpoint.vue
Normal file
25
KTUSAPS/ClientApp/src/pages/OidcEndpoint.vue
Normal 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>
|
23
KTUSAPS/ClientApp/src/router/index.js
Normal file
23
KTUSAPS/ClientApp/src/router/index.js
Normal 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
|
12
KTUSAPS/ClientApp/src/store/index.js
Normal file
12
KTUSAPS/ClientApp/src/store/index.js
Normal 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()] : [],
|
||||
});
|
145
KTUSAPS/ClientApp/src/store/modules/auth.js
Normal file
145
KTUSAPS/ClientApp/src/store/modules/auth.js
Normal 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,
|
||||
}
|
8
KTUSAPS/ClientApp/vue.config.js
Normal file
8
KTUSAPS/ClientApp/vue.config.js
Normal 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");
|
||||
},
|
||||
};
|
Reference in New Issue
Block a user