Sfoglia il codice sorgente

feat: auth strategies over GraphQL + svg loading

NGPixel 7 anni fa
parent
commit
501f0a9a53

File diff suppressed because it is too large
+ 0 - 0
assets/svg/auth-icon-azure.svg


+ 8 - 0
assets/svg/auth-icon-local.svg

@@ -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>

+ 10 - 1
client/js/app.js

@@ -3,6 +3,8 @@
 /* global siteConfig */
 /* eslint-disable no-new */
 
+import CONSTANTS from './constants'
+
 import Vue from 'vue'
 import VueResource from 'vue-resource'
 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 sourceViewComponent from './pages/source-view.component.js'
 
+// ====================================
+// Initialize Global Vars
+// ====================================
+
+window.wiki = null
+window.CONSTANTS = CONSTANTS
+
 // ====================================
 // Initialize Apollo Client (GraphQL)
 // ====================================
 
-window.apollo = new ApolloClient({
+window.graphQL = new ApolloClient({
   networkInterface: createBatchingNetworkInterface({
     uri: window.location.protocol + '//' + window.location.host + siteConfig.path + '/graphql'
   }),

+ 28 - 8
client/js/components/login.vue

@@ -7,11 +7,8 @@
           | {{ error.title }}
         span {{ error.message }}
       .login-providers(v-show='strategies.length > 1')
-        button.is-active(:title='$t("auth:providers.local")')
-          i.nc-icon-outline.ui-1_database
-          span {{ $t('auth:providers.local') }}
-        button(v-for='strategy in strategies', @onclick='selectProvider(strategy.key, strategy.useForm)', :title='strategy.title')
-          //-!= strategy.icon
+        button(v-for='strategy in strategies', :class='{ "is-active": strategy.key === selectedStrategy }', @click='selectStrategy(strategy.key, strategy.useForm)', :title='strategy.title')
+          em(v-html='strategy.icon')
           span {{ strategy.title }}
       .login-frame
         h1 {{ siteTitle }}
@@ -32,7 +29,8 @@ export default {
   data() {
     return {
       error: false,
-      strategies: []
+      strategies: [],
+      selectedStrategy: 'local'
     }
   },
   computed: {
@@ -41,9 +39,31 @@ export default {
     }
   },
   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>

+ 22 - 0
client/js/constants/graphql.js

@@ -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 - 0
client/js/constants/index.js

@@ -0,0 +1,5 @@
+import GRAPHQL from './graphql'
+
+export default {
+  GRAPHQL
+}

+ 7 - 12
client/js/modules/localization.js

@@ -1,11 +1,10 @@
 import i18next from 'i18next'
 import i18nextXHR from 'i18next-xhr-backend'
 import i18nextCache from 'i18next-localstorage-cache'
-import gql from 'graphql-tag'
 import VueI18Next from '@panter/vue-i18next'
 import loSet from 'lodash/set'
 
-/* global siteConfig */
+/* global siteConfig, graphQL, CONSTANTS */
 
 module.exports = {
   VueI18Next,
@@ -19,16 +18,12 @@ module.exports = {
           parse: (data) => data,
           ajax: (url, opts, cb, data) => {
             let langParams = url.split('/')
-            console.info(langParams)
-            window.apollo.query({
-              query: gql`
-                {
-                  translations(locale:"${langParams[0]}", namespace:"${langParams[1]}") {
-                    key
-                    value
-                  }
-                }
-              `
+            graphQL.query({
+              query: CONSTANTS.GRAPHQL.GQL_QUERY_TRANSLATIONS,
+              variables: {
+                locale: langParams[0],
+                namespace: langParams[1]
+              }
             }).then(resp => {
               let ns = {}
               if (resp.data.translations.length > 0) {

+ 5 - 1
client/scss/components/button.scss

@@ -61,7 +61,11 @@
 				background-color: mc($color,'800');
 				color: #FFF;
 				animation: none;
-			}
+      }
+
+      &:focus {
+        box-shadow: inset 0 0 0 3px rgba(255,255,255, .4);
+      }
 		}
 
 	}

+ 27 - 3
client/scss/pages/_login.scss

@@ -35,6 +35,7 @@
 
       .login-frame {
         border-radius: 0 6px 6px 0;
+        border-left: none;
       }
     }
 
@@ -104,6 +105,10 @@
       align-items: center;
       transition: all .4s ease;
 
+      &:focus {
+        outline: none;
+      }
+
       @include until($tablet) {
         justify-content: center;
       }
@@ -114,12 +119,24 @@
 
       &:first-child {
         border-top: none;
+
+        &.is-active {
+          border-top: 1px solid rgba(255,255,255, .5);
+        }
       }
 
       &.is-active {
-        background-color: mc('grey', '100');
-        background-image: radial-gradient(circle at top left, rgba(mc('grey', '200'),1) 0%,rgba(255,255,255,1) 100%);
+        background-image: linear-gradient(to right, rgba(255,255,255,1) 0%,rgba(255,255,255,.77) 100%);
         color: mc('light-blue', '700');
+        cursor: default;
+
+        &:hover {
+          background-color: transparent;
+        }
+
+        svg path {
+          fill: mc('light-blue', '800');
+        }
       }
 
       i {
@@ -160,7 +177,8 @@
   }
 
   &-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;
     width: 400px;
     padding: 1rem;
@@ -178,6 +196,7 @@
       font-size: 2rem;
       font-weight: 600;
       color: mc('light-blue', '700');
+      text-shadow: 1px 1px 0 #FFF;
       padding: 0;
       margin: 0;
     }
@@ -186,6 +205,7 @@
       font-size: 1.5rem;
       font-weight: 300;
       color: mc('grey', '700');
+      text-shadow: 1px 1px 0 #FFF;
       padding: 0;
       margin: 0 0 25px 0;
     }
@@ -200,6 +220,7 @@
       border: 1px solid #FFF;
       border-radius: 3px;
       background-color: rgba(255,255,255,.9);
+      box-shadow: inset 0 0 0 3px rgba(255,255,255, .25);
       padding: 0 15px;
       height: 40px;
       margin: 0 0 10px 0;
@@ -212,6 +233,9 @@
       &:focus {
         outline: none;
         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');
       }
 
     }

+ 1 - 1
server/authentication/microsoft.js

@@ -14,7 +14,7 @@ module.exports = {
   useForm: false,
   props: ['clientId', 'clientSecret', 'callbackURL'],
   init (passport, conf) {
-    passport.use('windowslive',
+    passport.use('microsoft',
       new WindowsLiveStrategy({
         clientID: conf.clientId,
         clientSecret: conf.clientSecret,

+ 2 - 0
server/modules/graphql.js

@@ -10,6 +10,7 @@ const _ = require('lodash')
 const typeDefs = fs.readFileSync(path.join(wiki.SERVERPATH, 'schemas/types.graphql'), 'utf8')
 
 const DateScalar = require('../schemas/scalar-date')
+const AuthenticationResolvers = require('../schemas/resolvers-authentication')
 const CommentResolvers = require('../schemas/resolvers-comment')
 const DocumentResolvers = require('../schemas/resolvers-document')
 const FileResolvers = require('../schemas/resolvers-file')
@@ -21,6 +22,7 @@ const TranslationResolvers = require('../schemas/resolvers-translation')
 const UserResolvers = require('../schemas/resolvers-user')
 
 const resolvers = _.merge(
+  AuthenticationResolvers,
   CommentResolvers,
   DocumentResolvers,
   FileResolvers,

+ 40 - 0
server/schemas/resolvers-authentication.js

@@ -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
+      })
+    }
+  }
+}

+ 2 - 1
server/schemas/types.graphql

@@ -32,10 +32,11 @@ interface Base {
 # TYPES
 
 type AuthenticationProvider {
-  id: String!
+  key: String!
   useForm: Boolean!
   title: String!
   props: [String]
+  icon: String
   config: String
 }
 

Some files were not shown because too many files changed in this diff