Skip to content

Commit

Permalink
[full-ci] ADD VUE history mode (#6363)
Browse files Browse the repository at this point in the history
* add option to use vues history mode

Co-authored-by: Benedikt Kulmann <[email protected]>
Co-authored-by: saw-jan <[email protected]>
  • Loading branch information
3 people authored Feb 22, 2022
1 parent e4e81aa commit 7ced02b
Show file tree
Hide file tree
Showing 22 changed files with 227 additions and 191 deletions.
2 changes: 1 addition & 1 deletion .drone.star
Original file line number Diff line number Diff line change
Expand Up @@ -2019,7 +2019,7 @@ def setUpOauth2(forIntegrationApp, disableFileLocking):
"make vendor",
"cd %s || exit" % dir["server"],
"php occ a:e oauth2",
"php occ oauth2:add-client Web Cxfj9F9ZZWQbQZps1E1M0BszMz6OOFq3lxjSuc8Uh4HLEYb9KIfyRMmgY5ibXXrU 930C6aA0U1VhM03IfNiheR2EwSzRi4hRSpcNqIhhbpeSGU6h38xssVfNcGP0sSwQ %s" % oidcURL,
"php occ oauth2:add-client Web Cxfj9F9ZZWQbQZps1E1M0BszMz6OOFq3lxjSuc8Uh4HLEYb9KIfyRMmgY5ibXXrU 930C6aA0U1VhM03IfNiheR2EwSzRi4hRSpcNqIhhbpeSGU6h38xssVfNcGP0sSwQ %s false true" % oidcURL,
]

if (disableFileLocking):
Expand Down
8 changes: 8 additions & 0 deletions changelog/unreleased/enhancement-history-mode
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Enhancement: Option to enable Vue history mode

We've added the option to use vue's history mode. All configuration is done automatically by the system.
To enable it, add a `<base src="PATH">` header tag to `index.html`, `oidc-callback.html` and `oidc-silent-redirect.html`.
Adding `<base>` is not needed for ocis.

https://github.com/owncloud/web/issues/6363
https://github.com/owncloud/web/issues/6277
6 changes: 6 additions & 0 deletions changelog/unreleased/enhancement-sidecar-mode
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Enhancement: Run web as oc10 sidecar

We've added the option to run web in oc10 sidecar mode.
Copy `config/config.json.sample-oc10` to `config/config.json`, run `yarn server` and then `docker compose up oc10`.

https://github.com/owncloud/web/issues/6363
2 changes: 1 addition & 1 deletion config/config.json.sample-oc10
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"theme": "https://localhost:9100/themes/owncloud/theme.json",
"version": "0.1.0",
"auth": {
"clientId": "",
"clientId": "UmCVsEIxdWmssxa6uVRRPC3txYBVN4qqxooJbsPhuuoPmHk9Pt9Oy68N4ZaKXUYy",
"url": "http://localhost:8080/index.php/apps/oauth2/api/v1/token",
"authUrl": "http://localhost:8080/index.php/apps/oauth2/authorize"
},
Expand Down
8 changes: 8 additions & 0 deletions dev/docker/oc10.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
ARG OC10_IMAGE
FROM ${OC10_IMAGE}

RUN apt -qqy update \
&& apt -qqy --no-install-recommends install \
bash \
&& rm -rf /var/lib/apt/lists/* \
&& apt -qyy clean
18 changes: 16 additions & 2 deletions dev/docker/oc10.entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,28 @@ done

if [ ! -d /mnt/data/apps/testing ]
then
# testing app
git clone https://github.com/owncloud/testing.git /mnt/data/apps/testing
occ app:enable oauth2
occ app:enable testing
# oauth2 app
rm -rf /var/www/owncloud/apps/oauth2
git clone https://github.com/owncloud/oauth2.git /var/www/owncloud/apps/oauth2
make -C /var/www/owncloud/apps/oauth2 vendor
occ app:enable oauth2
occ oauth2:add-client \
web \
M8W5mo3wQV3VHWYsaYpWhkr8dwa949i4GljCkedHhl7GWqmHMkxSeJgK2PcS0jt5 \
sqvPYXK94tMsEEVOYORxg8Ufesi2kC4WpJJSYb0Kj1DSAYl6u2XvJZjc3VcitjDv \
http://host.docker.internal:8080/index.php/apps/web/oidc-callback.html
http://host.docker.internal:8080/index.php/apps/web/oidc-callback.html \
false \
true
occ oauth2:add-client \
web-sidecar \
UmCVsEIxdWmssxa6uVRRPC3txYBVN4qqxooJbsPhuuoPmHk9Pt9Oy68N4ZaKXUYy \
HW1fo6lbtgEERBQufBouJ4HID2QaDfngvIdc2vjDUE46qKB4JRG1YDir41LliReC \
http://localhost:9100/oidc-callback.html
occ config:system:set trusted_domains 0 --value="localhost"
occ config:system:set cors.allowed-domains 0 --value="http://localhost:9100"
fi

if [ -d /var/www/owncloud/apps/web/ ]
Expand Down
6 changes: 5 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ services:
- host.docker.internal:${DOCKER_HOST:-host-gateway}

oc10:
image: ${OC10_IMAGE:-owncloud/server:latest}
build:
dockerfile: oc10.Dockerfile
context: ./dev/docker
args:
OC10_IMAGE: ${OC10_IMAGE:-owncloud/server:latest}
container_name: web_oc10
ports:
- 8080:8080
Expand Down
13 changes: 8 additions & 5 deletions packages/web-container/oidc-callback.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<body>
<script>
window.onload = function() {
window.location.href = window.location.origin +
window.location.pathname.substr(0, window.location.pathname.length - 18) +
"index.html#/oidc-callback" + window.location.search;
}
window.onload = function () {
const base = document.querySelector('base')
const path = base ? new URL(base.href).pathname.split('/') : [...window.location.pathname.split('/').slice(0, -1), 'index.html#']
const url = new URL([...path, 'oidc-callback'].filter(Boolean).join('/'), window.location.origin)
window.location.href = url.href + window.location.search
}
</script>
</body>
</html>
13 changes: 8 additions & 5 deletions packages/web-container/oidc-silent-redirect.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<body>
<script>
window.onload = function() {
window.location.href = window.location.origin +
window.location.pathname.substr(0, window.location.pathname.length - 25) +
"index.html#/oidc-silent-redirect" + window.location.search;
}
window.onload = function () {
const base = document.querySelector('base')
const path = base ? new URL(base.href).pathname.split('/') : [...window.location.pathname.split('/').slice(0, -1), 'index.html#']
const url = new URL([...path, 'oidc-silent-redirect'].filter(Boolean).join('/'), window.location.origin)
window.location.href = url.href + window.location.search
}
</script>
</body>
</html>
2 changes: 1 addition & 1 deletion packages/web-runtime/src/defaults/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import '@fortawesome/fontawesome-free/attribution'

export { default as Vue } from './vue'
export { default as DesignSystem } from 'owncloud-design-system'
export { default as Router } from '../router'

export const store = createStore(Vuex.Store, { ...Store })
export const pages = { success: App, failure: missingOrInvalidConfigPage }
export const translations = merge({}, coreTranslations, odsTranslations)
Expand Down
4 changes: 3 additions & 1 deletion packages/web-runtime/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import {
translations,
supportedLanguages,
store,
Router as router,
Vue
} from './defaults'

import { router } from './router'

import {
requestConfiguration,
announceApplications,
Expand Down
203 changes: 117 additions & 86 deletions packages/web-runtime/src/router/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,67 +10,131 @@ import Account from '../pages/account.vue'

Vue.use(Router)

// type: patch
// temporary patch till we have upgraded web to the latest vue router which make this obsolete
// this takes care that routes like 'foo/bar/baz' which by default would be converted to 'foo%2Fbar%2Fbaz' stay as they are
// should immediately go away and be removed after finalizing the update
// to apply the patch to a route add meta.patchCleanPath = true to it
// to patch needs to be enabled on a route level, to do so add meta.patchCleanPath = true property to the route
const patchRouter = (router) => {
const bindMatcher = router.match.bind(router)
const cleanPath = (route) =>
[
['%2F', '/'],
['//', '/']
].reduce((path, rule) => path.replaceAll(rule[0], rule[1]), route || '')

router.match = (raw, current, redirectFrom) => {
const bindMatch = bindMatcher(raw, current, redirectFrom)

if (!get(bindMatch, 'meta.patchCleanPath', false)) {
return bindMatch
}

return {
...bindMatch,
path: cleanPath(bindMatch.path),
fullPath: cleanPath(bindMatch.fullPath)
}
}

return router
}

// just a dummy function to trick gettext tools
function $gettext(msg) {
return msg
}

const router = new Router({
// mode: 'history',
parseQuery(query) {
return qs.parse(query)
},
stringifyQuery(obj) {
return qs.stringify(obj, {
allowDots: true,
addQueryPrefix: true
})
},
routes: [
{
path: '/login',
name: 'login',
component: LoginPage,
meta: { auth: false, title: $gettext('Login') }
},
{
path: '/oidc-callback',
name: 'oidcCallback',
component: OidcCallbackPage,
meta: { auth: false, title: $gettext('Oidc callback') }
},
{
path: '/oidc-silent-redirect',
name: 'oidcSilentRedirect',
component: OidcCallbackPage,
meta: { auth: false, title: $gettext('Oidc redirect') }
},
{
path: '/f/:fileId',
name: 'privateLink',
redirect: '/files/ops/resolver/private-link/:fileId',
meta: { title: $gettext('Private link') }
},
{
path: '/s/:token',
name: 'publicLink',
redirect: '/files/ops/resolver/public-link/:token',
meta: { auth: false, title: $gettext('Public link') }
const base = document.querySelector('base')
export const router = patchRouter(
new Router({
parseQuery(query) {
return qs.parse(query)
},
{
path: '/access-denied',
name: 'accessDenied',
component: AccessDeniedPage,
meta: { auth: false, title: $gettext('Access denied') }
stringifyQuery(obj) {
return qs.stringify(obj, {
allowDots: true,
addQueryPrefix: true
})
},
{
path: '/account',
name: 'account',
component: Account,
meta: { title: $gettext('Account') }
...(base && {
mode: 'history',
base: new URL(base.href).pathname
}),
routes: [
{
path: '/login',
name: 'login',
component: LoginPage,
meta: { auth: false, title: $gettext('Login') }
},
{
path: '/oidc-callback',
component: OidcCallbackPage,
meta: { auth: false, title: $gettext('Oidc callback') }
},
{
path: '/oidc-silent-redirect',
component: OidcCallbackPage,
meta: { auth: false, title: $gettext('Oidc redirect') }
},
{
path: '/f/:fileId',
name: 'privateLink',
redirect: '/files/ops/resolver/private-link/:fileId',
meta: { title: $gettext('Private link') }
},
{
path: '/s/:token',
name: 'publicLink',
redirect: '/files/ops/resolver/public-link/:token',
meta: { auth: false, title: $gettext('Public link') }
},
{
path: '/access-denied',
name: 'accessDenied',
component: AccessDeniedPage,
meta: { auth: false, title: $gettext('Access denied') }
},
{
path: '/account',
name: 'account',
component: Account,
meta: { title: $gettext('Account') }
}
]
})
)

export const buildUrl = (pathname) => {
const isHistoryMode = !!base
const baseUrl = new URL(window.location.href.split('#')[0])
if (isHistoryMode) {
// in history mode we can't determine the base path, it must be provided by the document
baseUrl.pathname = new URL(base.href).pathname
} else {
// in hash mode, auto-determine the base path by removing `/index.html`
if (baseUrl.pathname.endsWith('/index.html')) {
baseUrl.pathname = baseUrl.pathname.split('/').slice(0, -1).filter(Boolean).join('/')
}
]
})
}

/**
* build full url by either
* - concatenating baseUrl and pathname (for unknown/non-router urls, e.g. `oidc-callback.html`) or
* - resolving via router (for known routes)
*/
if (/\.(html?)$/i.test(pathname)) {
baseUrl.pathname = [...baseUrl.pathname.split('/'), ...pathname.split('/')]
.filter(Boolean)
.join('/')
} else {
baseUrl[isHistoryMode ? 'pathname' : 'hash'] = router.resolve(pathname).href
}

return baseUrl.href
}

router.beforeEach(function (to, from, next) {
const store = Vue.$store
Expand Down Expand Up @@ -119,36 +183,3 @@ const isAuthRequired = (router, to) => {
}
return false
}

// type: patch
// temporary patch till we have upgraded web to the latest vue router which make this obsolete
// this takes care that routes like 'foo/bar/baz' which by default would be converted to 'foo%2Fbar%2Fbaz' stay as they are
// should immediately go away and be removed after finalizing the update
// to apply the patch to a route add meta.patchCleanPath = true to it
// to patch needs to be enabled on a route level, to do so add meta.patchCleanPath = true property to the route
const patchRouter = (router) => {
const bindMatcher = router.match.bind(router)
const cleanPath = (route) =>
[
['%2F', '/'],
['//', '/']
].reduce((path, rule) => path.replaceAll(rule[0], rule[1]), route || '')

router.match = (raw, current, redirectFrom) => {
const bindMatch = bindMatcher(raw, current, redirectFrom)

if (!get(bindMatch, 'meta.patchCleanPath', false)) {
return bindMatch
}

return {
...bindMatch,
path: cleanPath(bindMatch.path),
fullPath: cleanPath(bindMatch.fullPath)
}
}

return router
}

export default patchRouter(router)
11 changes: 4 additions & 7 deletions packages/web-runtime/src/services/auth.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,23 @@
import { Log, User, UserManager, WebStorageStateStore } from 'oidc-client'
import { buildUrl } from '../router'

export function initVueAuthenticate(config) {
if (config) {
const store = new WebStorageStateStore({
prefix: 'oc_oAuth',
store: sessionStorage
})
let baseUrl = window.location.href.split('#')[0]
if (baseUrl.endsWith('/index.html')) {
baseUrl = baseUrl.substr(0, baseUrl.length - 10)
}
const openIdConfig = {
userStore: store,
redirect_uri: baseUrl + 'oidc-callback.html',
redirect_uri: buildUrl('/oidc-callback.html'),
response_type: 'code', // code triggers auth code grant flow
response_mode: 'query',
scope: 'openid profile offline_access',
monitorSession: false,
// set uri directly to the login route to prevent problems with query parameters.
// See https://github.com/owncloud/web/issues/3285
post_logout_redirect_uri: baseUrl + '#/login',
silent_redirect_uri: baseUrl + 'oidc-silent-redirect.html',
post_logout_redirect_uri: buildUrl('/login'),
silent_redirect_uri: buildUrl('/oidc-silent-redirect.html'),
accessTokenExpiringNotificationTime: 10,
automaticSilentRenew: true,
filterProtocolClaims: true,
Expand Down
Loading

0 comments on commit 7ced02b

Please sign in to comment.