feat: auth strategies over GraphQL + svg loading
This commit is contained in:
parent
51f5c236d6
commit
501f0a9a53
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 1.9 KiB |
8
assets/svg/auth-icon-local.svg
Normal file
8
assets/svg/auth-icon-local.svg
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="64px" height="64px" viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve">
|
||||||
|
<path d="M32,19c13.089,0,27-3.154,27-9S45.089,1,32,1S5,4.154,5,10S18.911,19,32,19z"/>
|
||||||
|
<path d="M32,41c13.089,0,27-3.154,27-9V14.436c-1.481,1.493-3.963,2.968-8.022,4.174C45.864,20.128,38.946,21,32,21
|
||||||
|
s-13.864-0.872-18.978-2.391C8.963,17.403,6.481,15.929,5,14.436V32C5,37.846,18.911,41,32,41z"/>
|
||||||
|
<path d="M32,63c13.089,0,27-3.154,27-9V36.436c-1.481,1.493-3.963,2.968-8.022,4.174C45.864,42.128,38.946,43,32,43
|
||||||
|
s-13.864-0.872-18.978-2.391C8.963,39.403,6.481,37.929,5,36.436V54C5,59.846,18.911,63,32,63z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 729 B |
@ -3,6 +3,8 @@
|
|||||||
/* global siteConfig */
|
/* global siteConfig */
|
||||||
/* eslint-disable no-new */
|
/* eslint-disable no-new */
|
||||||
|
|
||||||
|
import CONSTANTS from './constants'
|
||||||
|
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import VueResource from 'vue-resource'
|
import VueResource from 'vue-resource'
|
||||||
import VueClipboards from 'vue-clipboards'
|
import VueClipboards from 'vue-clipboards'
|
||||||
@ -54,11 +56,18 @@ import contentViewComponent from './pages/content-view.component.js'
|
|||||||
import editorComponent from './components/editor.component.js'
|
import editorComponent from './components/editor.component.js'
|
||||||
import sourceViewComponent from './pages/source-view.component.js'
|
import sourceViewComponent from './pages/source-view.component.js'
|
||||||
|
|
||||||
|
// ====================================
|
||||||
|
// Initialize Global Vars
|
||||||
|
// ====================================
|
||||||
|
|
||||||
|
window.wiki = null
|
||||||
|
window.CONSTANTS = CONSTANTS
|
||||||
|
|
||||||
// ====================================
|
// ====================================
|
||||||
// Initialize Apollo Client (GraphQL)
|
// Initialize Apollo Client (GraphQL)
|
||||||
// ====================================
|
// ====================================
|
||||||
|
|
||||||
window.apollo = new ApolloClient({
|
window.graphQL = new ApolloClient({
|
||||||
networkInterface: createBatchingNetworkInterface({
|
networkInterface: createBatchingNetworkInterface({
|
||||||
uri: window.location.protocol + '//' + window.location.host + siteConfig.path + '/graphql'
|
uri: window.location.protocol + '//' + window.location.host + siteConfig.path + '/graphql'
|
||||||
}),
|
}),
|
||||||
|
@ -7,11 +7,8 @@
|
|||||||
| {{ error.title }}
|
| {{ error.title }}
|
||||||
span {{ error.message }}
|
span {{ error.message }}
|
||||||
.login-providers(v-show='strategies.length > 1')
|
.login-providers(v-show='strategies.length > 1')
|
||||||
button.is-active(:title='$t("auth:providers.local")')
|
button(v-for='strategy in strategies', :class='{ "is-active": strategy.key === selectedStrategy }', @click='selectStrategy(strategy.key, strategy.useForm)', :title='strategy.title')
|
||||||
i.nc-icon-outline.ui-1_database
|
em(v-html='strategy.icon')
|
||||||
span {{ $t('auth:providers.local') }}
|
|
||||||
button(v-for='strategy in strategies', @onclick='selectProvider(strategy.key, strategy.useForm)', :title='strategy.title')
|
|
||||||
//-!= strategy.icon
|
|
||||||
span {{ strategy.title }}
|
span {{ strategy.title }}
|
||||||
.login-frame
|
.login-frame
|
||||||
h1 {{ siteTitle }}
|
h1 {{ siteTitle }}
|
||||||
@ -32,7 +29,8 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
error: false,
|
error: false,
|
||||||
strategies: []
|
strategies: [],
|
||||||
|
selectedStrategy: 'local'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -41,9 +39,31 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
selectProvider(key, useForm) {
|
selectStrategy(key, useForm) {
|
||||||
|
this.selectedStrategy = key
|
||||||
|
if (!useForm) {
|
||||||
|
window.location.assign(siteConfig.path + '/login/' + key)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
refreshStrategies() {
|
||||||
|
graphQL.query({
|
||||||
|
query: CONSTANTS.GRAPHQL.GQL_QUERY_AUTHENTICATION,
|
||||||
|
variables: {
|
||||||
|
mode: 'active'
|
||||||
|
}
|
||||||
|
}).then(resp => {
|
||||||
|
if (resp.data.authentication) {
|
||||||
|
this.strategies = resp.data.authentication
|
||||||
|
} else {
|
||||||
|
throw new Error('No authentication providers available!')
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
console.error(err)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.refreshStrategies()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
22
client/js/constants/graphql.js
Normal file
22
client/js/constants/graphql.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import gql from 'graphql-tag'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
GQL_QUERY_AUTHENTICATION: gql`
|
||||||
|
query($mode: String!) {
|
||||||
|
authentication(mode:$mode) {
|
||||||
|
key
|
||||||
|
useForm
|
||||||
|
title
|
||||||
|
icon
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
GQL_QUERY_TRANSLATIONS: gql`
|
||||||
|
query($locale: String!, $namespace: String!) {
|
||||||
|
translations(locale:$locale, namespace:$namespace) {
|
||||||
|
key
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
5
client/js/constants/index.js
Normal file
5
client/js/constants/index.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import GRAPHQL from './graphql'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
GRAPHQL
|
||||||
|
}
|
@ -1,11 +1,10 @@
|
|||||||
import i18next from 'i18next'
|
import i18next from 'i18next'
|
||||||
import i18nextXHR from 'i18next-xhr-backend'
|
import i18nextXHR from 'i18next-xhr-backend'
|
||||||
import i18nextCache from 'i18next-localstorage-cache'
|
import i18nextCache from 'i18next-localstorage-cache'
|
||||||
import gql from 'graphql-tag'
|
|
||||||
import VueI18Next from '@panter/vue-i18next'
|
import VueI18Next from '@panter/vue-i18next'
|
||||||
import loSet from 'lodash/set'
|
import loSet from 'lodash/set'
|
||||||
|
|
||||||
/* global siteConfig */
|
/* global siteConfig, graphQL, CONSTANTS */
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
VueI18Next,
|
VueI18Next,
|
||||||
@ -19,16 +18,12 @@ module.exports = {
|
|||||||
parse: (data) => data,
|
parse: (data) => data,
|
||||||
ajax: (url, opts, cb, data) => {
|
ajax: (url, opts, cb, data) => {
|
||||||
let langParams = url.split('/')
|
let langParams = url.split('/')
|
||||||
console.info(langParams)
|
graphQL.query({
|
||||||
window.apollo.query({
|
query: CONSTANTS.GRAPHQL.GQL_QUERY_TRANSLATIONS,
|
||||||
query: gql`
|
variables: {
|
||||||
{
|
locale: langParams[0],
|
||||||
translations(locale:"${langParams[0]}", namespace:"${langParams[1]}") {
|
namespace: langParams[1]
|
||||||
key
|
}
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
}).then(resp => {
|
}).then(resp => {
|
||||||
let ns = {}
|
let ns = {}
|
||||||
if (resp.data.translations.length > 0) {
|
if (resp.data.translations.length > 0) {
|
||||||
|
@ -61,7 +61,11 @@
|
|||||||
background-color: mc($color,'800');
|
background-color: mc($color,'800');
|
||||||
color: #FFF;
|
color: #FFF;
|
||||||
animation: none;
|
animation: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
box-shadow: inset 0 0 0 3px rgba(255,255,255, .4);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
|
|
||||||
.login-frame {
|
.login-frame {
|
||||||
border-radius: 0 6px 6px 0;
|
border-radius: 0 6px 6px 0;
|
||||||
|
border-left: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,6 +105,10 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
transition: all .4s ease;
|
transition: all .4s ease;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
@include until($tablet) {
|
@include until($tablet) {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
@ -114,12 +119,24 @@
|
|||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
border-top: none;
|
border-top: none;
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
border-top: 1px solid rgba(255,255,255, .5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-active {
|
&.is-active {
|
||||||
background-color: mc('grey', '100');
|
background-image: linear-gradient(to right, rgba(255,255,255,1) 0%,rgba(255,255,255,.77) 100%);
|
||||||
background-image: radial-gradient(circle at top left, rgba(mc('grey', '200'),1) 0%,rgba(255,255,255,1) 100%);
|
|
||||||
color: mc('light-blue', '700');
|
color: mc('light-blue', '700');
|
||||||
|
cursor: default;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg path {
|
||||||
|
fill: mc('light-blue', '800');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
i {
|
i {
|
||||||
@ -160,7 +177,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-frame {
|
&-frame {
|
||||||
background-image: radial-gradient(circle at top left, rgba(255,255,255,1) 5%,rgba(240,240,240,.6) 100%);
|
background-image: radial-gradient(circle at top center, rgba(255,255,255,1) 5%,rgba(255,255,255,.6) 100%);
|
||||||
|
border: 1px solid rgba(255,255,255, .5);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
width: 400px;
|
width: 400px;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
@ -178,6 +196,7 @@
|
|||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: mc('light-blue', '700');
|
color: mc('light-blue', '700');
|
||||||
|
text-shadow: 1px 1px 0 #FFF;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
@ -186,6 +205,7 @@
|
|||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
color: mc('grey', '700');
|
color: mc('grey', '700');
|
||||||
|
text-shadow: 1px 1px 0 #FFF;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0 0 25px 0;
|
margin: 0 0 25px 0;
|
||||||
}
|
}
|
||||||
@ -200,6 +220,7 @@
|
|||||||
border: 1px solid #FFF;
|
border: 1px solid #FFF;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
background-color: rgba(255,255,255,.9);
|
background-color: rgba(255,255,255,.9);
|
||||||
|
box-shadow: inset 0 0 0 3px rgba(255,255,255, .25);
|
||||||
padding: 0 15px;
|
padding: 0 15px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
margin: 0 0 10px 0;
|
margin: 0 0 10px 0;
|
||||||
@ -212,6 +233,9 @@
|
|||||||
&:focus {
|
&:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: mc('light-blue','500');
|
border-color: mc('light-blue','500');
|
||||||
|
background-color: rgba(255,255,255,1);
|
||||||
|
box-shadow: inset 0 0 0 3px rgba(mc('light-blue','500'), .25);
|
||||||
|
color: mc('light-blue', '800');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ module.exports = {
|
|||||||
useForm: false,
|
useForm: false,
|
||||||
props: ['clientId', 'clientSecret', 'callbackURL'],
|
props: ['clientId', 'clientSecret', 'callbackURL'],
|
||||||
init (passport, conf) {
|
init (passport, conf) {
|
||||||
passport.use('windowslive',
|
passport.use('microsoft',
|
||||||
new WindowsLiveStrategy({
|
new WindowsLiveStrategy({
|
||||||
clientID: conf.clientId,
|
clientID: conf.clientId,
|
||||||
clientSecret: conf.clientSecret,
|
clientSecret: conf.clientSecret,
|
||||||
|
@ -10,6 +10,7 @@ const _ = require('lodash')
|
|||||||
const typeDefs = fs.readFileSync(path.join(wiki.SERVERPATH, 'schemas/types.graphql'), 'utf8')
|
const typeDefs = fs.readFileSync(path.join(wiki.SERVERPATH, 'schemas/types.graphql'), 'utf8')
|
||||||
|
|
||||||
const DateScalar = require('../schemas/scalar-date')
|
const DateScalar = require('../schemas/scalar-date')
|
||||||
|
const AuthenticationResolvers = require('../schemas/resolvers-authentication')
|
||||||
const CommentResolvers = require('../schemas/resolvers-comment')
|
const CommentResolvers = require('../schemas/resolvers-comment')
|
||||||
const DocumentResolvers = require('../schemas/resolvers-document')
|
const DocumentResolvers = require('../schemas/resolvers-document')
|
||||||
const FileResolvers = require('../schemas/resolvers-file')
|
const FileResolvers = require('../schemas/resolvers-file')
|
||||||
@ -21,6 +22,7 @@ const TranslationResolvers = require('../schemas/resolvers-translation')
|
|||||||
const UserResolvers = require('../schemas/resolvers-user')
|
const UserResolvers = require('../schemas/resolvers-user')
|
||||||
|
|
||||||
const resolvers = _.merge(
|
const resolvers = _.merge(
|
||||||
|
AuthenticationResolvers,
|
||||||
CommentResolvers,
|
CommentResolvers,
|
||||||
DocumentResolvers,
|
DocumentResolvers,
|
||||||
FileResolvers,
|
FileResolvers,
|
||||||
|
40
server/schemas/resolvers-authentication.js
Normal file
40
server/schemas/resolvers-authentication.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
const _ = require('lodash')
|
||||||
|
const fs = require('fs-extra')
|
||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
/* global wiki */
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Query: {
|
||||||
|
authentication(obj, args, context, info) {
|
||||||
|
switch (args.mode) {
|
||||||
|
case 'active':
|
||||||
|
let strategies = _.chain(wiki.auth.strategies).map(str => {
|
||||||
|
return {
|
||||||
|
key: str.key,
|
||||||
|
title: str.title,
|
||||||
|
useForm: str.useForm
|
||||||
|
}
|
||||||
|
}).sortBy(['title']).value()
|
||||||
|
let localStrategy = _.remove(strategies, str => str.key === 'local')
|
||||||
|
return _.concat(localStrategy, strategies)
|
||||||
|
case 'all':
|
||||||
|
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Mutation: {},
|
||||||
|
AuthenticationProvider: {
|
||||||
|
icon (ap, args) {
|
||||||
|
return fs.readFileAsync(path.join(wiki.ROOTPATH, `assets/svg/auth-icon-${ap.key}.svg`), 'utf8').catch(err => {
|
||||||
|
if (err.code === 'ENOENT') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
throw err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -32,10 +32,11 @@ interface Base {
|
|||||||
# TYPES
|
# TYPES
|
||||||
|
|
||||||
type AuthenticationProvider {
|
type AuthenticationProvider {
|
||||||
id: String!
|
key: String!
|
||||||
useForm: Boolean!
|
useForm: Boolean!
|
||||||
title: String!
|
title: String!
|
||||||
props: [String]
|
props: [String]
|
||||||
|
icon: String
|
||||||
config: String
|
config: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user