feat: auth strategies over GraphQL + svg loading
This commit is contained in:
		
										
											
												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) { | ||||||
|   | |||||||
| @@ -62,6 +62,10 @@ | |||||||
| 				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 | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user