mirror of https://github.com/statping/statping
				
				
				
			fixed authentication via cookies, oauth login,
							parent
							
								
									2aa3bee623
								
							
						
					
					
						commit
						42e1b0216a
					
				| 
						 | 
				
			
			@ -33,6 +33,7 @@
 | 
			
		|||
    "vue-apexcharts": "^1.5.2",
 | 
			
		||||
    "vue-clipboard2": "^0.3.1",
 | 
			
		||||
    "vue-codemirror": "^4.0.6",
 | 
			
		||||
    "vue-cookies": "^1.7.0",
 | 
			
		||||
    "vue-flatpickr-component": "^8.1.5",
 | 
			
		||||
    "vue-github-button": "^1.1.2",
 | 
			
		||||
    "vue-moment": "^4.1.0",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ import * as Sentry from "@sentry/browser";
 | 
			
		|||
import * as Integrations from "@sentry/integrations";
 | 
			
		||||
const qs = require('querystring');
 | 
			
		||||
 | 
			
		||||
const tokenKey = "statping_user";
 | 
			
		||||
const tokenKey = "statping_auth";
 | 
			
		||||
const errorReporter = "https://bed4d75404924cb3a799e370733a1b64@sentry.statping.com/3"
 | 
			
		||||
 | 
			
		||||
class Api {
 | 
			
		||||
| 
						 | 
				
			
			@ -29,6 +29,10 @@ class Api {
 | 
			
		|||
    return axios.post('api/core', obj).then(response => (response.data))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async oauth_save(obj) {
 | 
			
		||||
    return axios.post('api/oauth', obj).then(response => (response.data))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async setup_save(data) {
 | 
			
		||||
    return axios.post('api/setup', qs.stringify(data)).then(response => (response.data))
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -228,19 +232,11 @@ class Api {
 | 
			
		|||
 | 
			
		||||
  async login(username, password) {
 | 
			
		||||
    const f = {username: username, password: password}
 | 
			
		||||
    return axios.post('api/login', qs.stringify(f))
 | 
			
		||||
        .then(response => (response.data))
 | 
			
		||||
    return axios.post('api/login', qs.stringify(f)).then(response => (response.data))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async logout() {
 | 
			
		||||
    await axios.get('api/logout').then(response => (response.data))
 | 
			
		||||
    return localStorage.removeItem(tokenKey)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  saveToken(username, token, admin) {
 | 
			
		||||
    const user = {username: username, token: token, admin: admin}
 | 
			
		||||
    localStorage.setItem(tokenKey, JSON.stringify(user));
 | 
			
		||||
    return user
 | 
			
		||||
    return axios.get('api/logout').then(response => (response.data))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async scss_base() {
 | 
			
		||||
| 
						 | 
				
			
			@ -255,17 +251,17 @@ class Api {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  token() {
 | 
			
		||||
    const tk = localStorage.getItem(tokenKey)
 | 
			
		||||
    const tk = $cookies.get(tokenKey)
 | 
			
		||||
    if (!tk) {
 | 
			
		||||
      return {};
 | 
			
		||||
      return {admin: false};
 | 
			
		||||
    }
 | 
			
		||||
    return JSON.parse(tk);
 | 
			
		||||
    return tk;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  authToken() {
 | 
			
		||||
    let user = JSON.parse(localStorage.getItem(tokenKey));
 | 
			
		||||
    if (user && user.token) {
 | 
			
		||||
      return {'Authorization': 'Bearer ' + user.token};
 | 
			
		||||
    const tk = $cookies.get(tokenKey)
 | 
			
		||||
    if (tk.token) {
 | 
			
		||||
      return {'Authorization': 'Bearer ' + tk.token};
 | 
			
		||||
    } else {
 | 
			
		||||
      return {};
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,22 +32,19 @@
 | 
			
		|||
        this.$router.push('/setup')
 | 
			
		||||
      }
 | 
			
		||||
    if (this.$route.path !== '/setup') {
 | 
			
		||||
      if (this.core.logged_in) {
 | 
			
		||||
      if (this.$store.state.admin) {
 | 
			
		||||
        await this.$store.dispatch('loadAdmin')
 | 
			
		||||
      } else {
 | 
			
		||||
        await this.$store.dispatch('loadRequired')
 | 
			
		||||
      }
 | 
			
		||||
      this.loaded = true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  },
 | 
			
		||||
    async mounted() {
 | 
			
		||||
          if (this.$route.path !== '/setup') {
 | 
			
		||||
              const tk = localStorage.getItem("statping_user")
 | 
			
		||||
              if (this.core.logged_in) {
 | 
			
		||||
            if (this.$store.state.admin) {
 | 
			
		||||
                this.logged_in = true
 | 
			
		||||
                  await this.$store.dispatch('loadAdmin')
 | 
			
		||||
                  // await this.$store.dispatch('loadAdmin')
 | 
			
		||||
              }
 | 
			
		||||
          }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,7 +28,7 @@
 | 
			
		|||
                </li>
 | 
			
		||||
            </ul>
 | 
			
		||||
            <span class="navbar-text">
 | 
			
		||||
      <a href="#" class="nav-link" v-on:click="logout">Logout</a>
 | 
			
		||||
      <a href="#" class="nav-link" @click.prevent="logout">Logout</a>
 | 
			
		||||
    </span>
 | 
			
		||||
        </div>
 | 
			
		||||
    </nav>
 | 
			
		||||
| 
						 | 
				
			
			@ -37,6 +37,7 @@
 | 
			
		|||
 | 
			
		||||
<script>
 | 
			
		||||
  import Api from "../../API"
 | 
			
		||||
  import Vue from "vue";
 | 
			
		||||
 | 
			
		||||
  export default {
 | 
			
		||||
  name: 'TopNav',
 | 
			
		||||
| 
						 | 
				
			
			@ -51,6 +52,7 @@
 | 
			
		|||
          this.$store.commit('setHasAllData', false)
 | 
			
		||||
          this.$store.commit('setToken', null)
 | 
			
		||||
          this.$store.commit('setAdmin', false)
 | 
			
		||||
          this.$cookies.remove("statping_auth")
 | 
			
		||||
          await this.$router.push('/logout')
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@
 | 
			
		|||
            <a href="https://github.com/statping/statping" target="_blank">
 | 
			
		||||
                Statping {{core.version}} made with <font-awesome-icon icon="heart"/>
 | 
			
		||||
            </a> |
 | 
			
		||||
            <router-link :to="core.logged_in ? '/dashboard' : '/login'">Dashboard</router-link>
 | 
			
		||||
            <router-link :to="$store.state.admin ? '/dashboard' : '/login'">Dashboard</router-link>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div v-else class="footer text-center mb-4 p-2" v-html="core.footer"></div>
 | 
			
		||||
    </footer>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,16 +25,16 @@
 | 
			
		|||
        </div>
 | 
			
		||||
    </form>
 | 
			
		||||
 | 
			
		||||
        <a v-if="oauth.gh_client_id" :href="GHlogin()" class="btn btn-block">
 | 
			
		||||
            Github Login
 | 
			
		||||
        <a v-if="oauth.gh_client_id" @click.prevent="GHlogin" href="#" class="btn btn-block btn-outline-dark">
 | 
			
		||||
            <font-awesome-icon :icon="['fab', 'github']" /> Login with Github
 | 
			
		||||
        </a>
 | 
			
		||||
 | 
			
		||||
        <a v-if="oauth.slack_client_id" :href="Slacklogin()" class="btn btn-block">
 | 
			
		||||
            Slack Login
 | 
			
		||||
        <a v-if="oauth.slack_client_id" @click.prevent="Slacklogin" href="#" class="btn btn-block btn-outline-dark">
 | 
			
		||||
            <font-awesome-icon :icon="['fab', 'slack']" /> Login with Slack
 | 
			
		||||
        </a>
 | 
			
		||||
 | 
			
		||||
        <a v-if="oauth.google_client_id" :href="Googlelogin()" class="btn btn-block">
 | 
			
		||||
            Google Login
 | 
			
		||||
        <a v-if="oauth.google_client_id" @click.prevent="Googlelogin" href="#" class="btn btn-block btn-outline-dark">
 | 
			
		||||
            <font-awesome-icon :icon="['fab', 'google']" /> Login with Google
 | 
			
		||||
        </a>
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
| 
						 | 
				
			
			@ -80,7 +80,8 @@
 | 
			
		|||
              if (auth.error) {
 | 
			
		||||
                  this.error = true
 | 
			
		||||
              } else if (auth.token) {
 | 
			
		||||
                  this.auth = Api.saveToken(this.username, auth.token, auth.admin)
 | 
			
		||||
                const u = {username: this.username, admin: auth.admin, token: auth.token}
 | 
			
		||||
                this.$cookies.set("statping_auth", JSON.stringify(u))
 | 
			
		||||
                  this.$store.dispatch('loadAdmin')
 | 
			
		||||
                  this.$store.commit('setAdmin', auth.admin)
 | 
			
		||||
                  this.$router.push('/dashboard')
 | 
			
		||||
| 
						 | 
				
			
			@ -88,13 +89,13 @@
 | 
			
		|||
              this.loading = false
 | 
			
		||||
          },
 | 
			
		||||
        GHlogin() {
 | 
			
		||||
          return `https://github.com/login/oauth/authorize?client_id=${this.oauth.gh_client_id}&redirect_uri=${this.core.domain}/api/oauth/github&scope=user,repo`
 | 
			
		||||
            window.location = `https://github.com/login/oauth/authorize?client_id=${this.oauth.gh_client_id}&redirect_uri=${this.core.domain}/oauth/github&scope=user,repo`
 | 
			
		||||
        },
 | 
			
		||||
        Slacklogin() {
 | 
			
		||||
          return `https://slack.com/oauth/authorize?client_id=${this.oauth.slack_client_id}&redirect_uri=${this.core.domain}/api/oauth/slack&scope=users.profile:read,users:read.email`
 | 
			
		||||
          window.location = `https://slack.com/oauth/authorize?client_id=${this.oauth.slack_client_id}&redirect_uri=${this.core.domain}/oauth/slack&scope=identity.basic`
 | 
			
		||||
        },
 | 
			
		||||
        Googlelogin() {
 | 
			
		||||
          return `https://accounts.google.com/signin/oauth?client_id=${this.oauth.google_client_id}&redirect_uri=${this.core.domain}/api/oauth/google&response_type=code&scope=https://www.googleapis.com/auth/userinfo.profile+https://www.googleapis.com/auth/userinfo.email`
 | 
			
		||||
          window.location = `https://accounts.google.com/signin/oauth?client_id=${this.oauth.google_client_id}&redirect_uri=${this.core.domain}/oauth/google&response_type=code&scope=https://www.googleapis.com/auth/userinfo.profile+https://www.googleapis.com/auth/userinfo.email`
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -162,7 +162,7 @@
 | 
			
		|||
        core() {
 | 
			
		||||
          return this.$store.getters.core
 | 
			
		||||
        },
 | 
			
		||||
        oauth() {
 | 
			
		||||
        auth() {
 | 
			
		||||
          return this.$store.getters.oauth
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
| 
						 | 
				
			
			@ -171,10 +171,22 @@
 | 
			
		|||
            google_enabled: false,
 | 
			
		||||
            slack_enabled: false,
 | 
			
		||||
            github_enabled: false,
 | 
			
		||||
            local_enabled: false
 | 
			
		||||
            local_enabled: false,
 | 
			
		||||
            oauth: {
 | 
			
		||||
              gh_client_id: "",
 | 
			
		||||
              gh_client_secret: "",
 | 
			
		||||
              google_client_id: "",
 | 
			
		||||
              google_client_secret: "",
 | 
			
		||||
              oauth_domains: "",
 | 
			
		||||
              oauth_providers: "",
 | 
			
		||||
              slack_client_id: "",
 | 
			
		||||
              slack_client_secret: "",
 | 
			
		||||
              slack_team: ""
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
      },
 | 
			
		||||
    mounted() {
 | 
			
		||||
        this.oauth = this.auth
 | 
			
		||||
      this.local_enabled = this.has('local')
 | 
			
		||||
      this.github_enabled = this.has('github')
 | 
			
		||||
      this.google_enabled = this.has('google')
 | 
			
		||||
| 
						 | 
				
			
			@ -207,10 +219,9 @@
 | 
			
		|||
            let c = this.core
 | 
			
		||||
            c.oauth = this.oauth
 | 
			
		||||
            c.oauth.oauth_providers = this.providers()
 | 
			
		||||
            await Api.core_save(c)
 | 
			
		||||
            const core = await Api.core()
 | 
			
		||||
            this.$store.commit('setCore', core)
 | 
			
		||||
            this.$store.commit('setOAuth', c.oauth)
 | 
			
		||||
            await Api.oauth_save(c)
 | 
			
		||||
            const oauth = await Api.oauth()
 | 
			
		||||
            this.$store.commit('setOAuth', oauth)
 | 
			
		||||
          }
 | 
			
		||||
      }
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,19 +3,23 @@ import VueRouter from 'vue-router'
 | 
			
		|||
import VueApexCharts from 'vue-apexcharts'
 | 
			
		||||
import VueObserveVisibility from 'vue-observe-visibility'
 | 
			
		||||
import VueClipboard from 'vue-clipboard2'
 | 
			
		||||
import VueCookies from 'vue-cookies'
 | 
			
		||||
 | 
			
		||||
import App from '@/App.vue'
 | 
			
		||||
import store from './store'
 | 
			
		||||
 | 
			
		||||
import router from './routes'
 | 
			
		||||
import "./mixin"
 | 
			
		||||
import "./icons"
 | 
			
		||||
 | 
			
		||||
Vue.component('apexchart', VueApexCharts)
 | 
			
		||||
 | 
			
		||||
Vue.use(VueClipboard);
 | 
			
		||||
Vue.use(VueRouter);
 | 
			
		||||
Vue.use(VueObserveVisibility);
 | 
			
		||||
Vue.use(VueCookies);
 | 
			
		||||
Vue.$cookies.config('3d')
 | 
			
		||||
 | 
			
		||||
import router from './routes'
 | 
			
		||||
import "./mixin"
 | 
			
		||||
import "./icons"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Vue.config.productionTip = false
 | 
			
		||||
new Vue({
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,7 +34,18 @@ const routes = [
 | 
			
		|||
    meta: {
 | 
			
		||||
      requiresAuth: true
 | 
			
		||||
    },
 | 
			
		||||
    beforeEnter: CheckAuth,
 | 
			
		||||
    beforeEnter: async (to, from, next) => {
 | 
			
		||||
      if (to.matched.some(record => record.meta.requiresAuth)) {
 | 
			
		||||
        let tk = await Api.token()
 | 
			
		||||
        if (to.path !== '/login' && !tk.admin) {
 | 
			
		||||
          next('/login')
 | 
			
		||||
          return
 | 
			
		||||
        }
 | 
			
		||||
        next()
 | 
			
		||||
      } else {
 | 
			
		||||
        next()
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    children: [{
 | 
			
		||||
      path: '',
 | 
			
		||||
      component: DashboardIndex,
 | 
			
		||||
| 
						 | 
				
			
			@ -135,9 +146,10 @@ const router = new VueRouter({
 | 
			
		|||
    routes
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
function CheckAuth(to, from, next) {
 | 
			
		||||
let CheckAuth = (to, from, next) => {
 | 
			
		||||
  if (to.matched.some(record => record.meta.requiresAuth)) {
 | 
			
		||||
    let item = localStorage.getItem("statping_user")
 | 
			
		||||
    let item = this.$cookies.get("statping_auth")
 | 
			
		||||
    window.console.log(item)
 | 
			
		||||
    if (to.path !== '/login' && !item) {
 | 
			
		||||
      next('/login')
 | 
			
		||||
      return
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -102,7 +102,6 @@ export default new Vuex.Store({
 | 
			
		|||
            state.hasPublicData = bool
 | 
			
		||||
        },
 | 
			
		||||
        setCore (state, core) {
 | 
			
		||||
          window.console.log('GETTING CORE')
 | 
			
		||||
            state.core = core
 | 
			
		||||
        },
 | 
			
		||||
        setToken (state, token) {
 | 
			
		||||
| 
						 | 
				
			
			@ -146,10 +145,11 @@ export default new Vuex.Store({
 | 
			
		|||
        },
 | 
			
		||||
      async loadCore(context) {
 | 
			
		||||
        const core = await Api.core()
 | 
			
		||||
        const token = await Api.token()
 | 
			
		||||
        context.commit("setCore", core);
 | 
			
		||||
        context.commit('setAdmin', core.admin)
 | 
			
		||||
        context.commit('setAdmin', token.admin)
 | 
			
		||||
        context.commit('setCore', core)
 | 
			
		||||
        context.commit('setUser', core.logged_in)
 | 
			
		||||
        context.commit('setUser', token.token!==undefined)
 | 
			
		||||
      },
 | 
			
		||||
        async loadRequired(context) {
 | 
			
		||||
            const groups = await Api.groups()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,7 +10,7 @@ module.exports = {
 | 
			
		|||
      },
 | 
			
		||||
      '/oauth': {
 | 
			
		||||
        logLevel: 'debug',
 | 
			
		||||
        target: 'http://0.0.0.0:8585'
 | 
			
		||||
        target: 'http://0.0.0.0:8585/oauth/'
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11838,6 +11838,11 @@ vue-codemirror@^4.0.6:
 | 
			
		|||
    codemirror "^5.41.0"
 | 
			
		||||
    diff-match-patch "^1.0.0"
 | 
			
		||||
 | 
			
		||||
vue-cookies@^1.7.0:
 | 
			
		||||
  version "1.7.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/vue-cookies/-/vue-cookies-1.7.0.tgz#dd8f147ff1579e0cbd5f352dc5dff2696d8760b8"
 | 
			
		||||
  integrity sha512-vuEUm6wYMMrFAHFCrkzIUAy8+MgPAbBGmYXnk2M6X6O2KHbMT1wuDD2izacmsSUp6ZM02e23MJRtPRobl88VMg==
 | 
			
		||||
 | 
			
		||||
vue-eslint-parser@^4.0.2:
 | 
			
		||||
  version "4.0.3"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-4.0.3.tgz#80cf162e484387b2640371ad21ba1f86e0c10a61"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										1
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										1
									
								
								go.mod
								
								
								
								
							| 
						 | 
				
			
			@ -15,6 +15,7 @@ require (
 | 
			
		|||
	github.com/gogo/protobuf v1.3.1 // indirect
 | 
			
		||||
	github.com/golang/protobuf v1.3.5
 | 
			
		||||
	github.com/gorilla/mux v1.7.4
 | 
			
		||||
	github.com/gorilla/securecookie v1.1.1
 | 
			
		||||
	github.com/gorilla/sessions v1.2.0
 | 
			
		||||
	github.com/jinzhu/gorm v1.9.12
 | 
			
		||||
	github.com/joho/godotenv v1.3.0
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,13 +27,7 @@ type apiResponse struct {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func apiIndexHandler(r *http.Request) interface{} {
 | 
			
		||||
	coreClone := *core.App
 | 
			
		||||
	_, err := getJwtToken(r)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		coreClone.LoggedIn = true
 | 
			
		||||
		coreClone.IsAdmin = IsAdmin(r)
 | 
			
		||||
	}
 | 
			
		||||
	return coreClone
 | 
			
		||||
	return core.App
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func apiRenewHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
| 
						 | 
				
			
			@ -50,6 +44,18 @@ func apiRenewHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		|||
	returnJson(output, w, r)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func apiUpdateOAuthHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	var c core.OAuth
 | 
			
		||||
	err := DecodeJSON(r, &c)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		sendErrorJson(err, w, r)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	app := core.App
 | 
			
		||||
	app.OAuth = c
 | 
			
		||||
	sendJsonAction(app.OAuth, "update", w, r)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func apiOAuthHandler(r *http.Request) interface{} {
 | 
			
		||||
	app := core.App
 | 
			
		||||
	return app.OAuth
 | 
			
		||||
| 
						 | 
				
			
			@ -78,7 +84,6 @@ func apiCoreHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		|||
	if c.Domain != app.Domain {
 | 
			
		||||
		app.Domain = c.Domain
 | 
			
		||||
	}
 | 
			
		||||
	app.OAuth = c.OAuth
 | 
			
		||||
	app.UseCdn = null.NewNullBool(c.UseCdn.Bool)
 | 
			
		||||
	app.AllowReports = null.NewNullBool(c.AllowReports.Bool)
 | 
			
		||||
	utils.SentryInit(nil, app.AllowReports.Bool)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,7 +15,9 @@ import (
 | 
			
		|||
 | 
			
		||||
func logoutHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	removeJwtToken(w)
 | 
			
		||||
	http.Redirect(w, r, basePath, http.StatusSeeOther)
 | 
			
		||||
	out := make(map[string]string)
 | 
			
		||||
	out["status"] = "success"
 | 
			
		||||
	returnJson(out, w, r)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func logsHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
| 
						 | 
				
			
			@ -163,7 +165,7 @@ func removeJwtToken(w http.ResponseWriter) {
 | 
			
		|||
	http.SetCookie(w, &http.Cookie{
 | 
			
		||||
		Name:    cookieKey,
 | 
			
		||||
		Value:   "",
 | 
			
		||||
		Expires: utils.Now(),
 | 
			
		||||
		Expires: time.Now(),
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -178,8 +180,10 @@ func setJwtToken(user *users.User, w http.ResponseWriter) (JwtClaim, string) {
 | 
			
		|||
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwtClaim)
 | 
			
		||||
	tokenString, err := token.SignedString([]byte(jwtKey))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		utils.Log.Errorln("error setting token: ", err)
 | 
			
		||||
		log.Errorln("error setting token: ", err)
 | 
			
		||||
	}
 | 
			
		||||
	user.Token = tokenString
 | 
			
		||||
	// set cookies
 | 
			
		||||
	http.SetCookie(w, &http.Cookie{
 | 
			
		||||
		Name:    cookieKey,
 | 
			
		||||
		Value:   tokenString,
 | 
			
		||||
| 
						 | 
				
			
			@ -195,9 +199,8 @@ func apiLoginHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		|||
 | 
			
		||||
	user, auth := users.AuthUser(username, password)
 | 
			
		||||
	if auth {
 | 
			
		||||
		utils.Log.Infoln(fmt.Sprintf("User %v logged in from IP %v", user.Username, r.RemoteAddr))
 | 
			
		||||
		log.Infoln(fmt.Sprintf("User %v logged in from IP %v", user.Username, r.RemoteAddr))
 | 
			
		||||
		claim, token := setJwtToken(user, w)
 | 
			
		||||
 | 
			
		||||
		resp := struct {
 | 
			
		||||
			Token   string `json:"token"`
 | 
			
		||||
			IsAdmin bool   `json:"admin"`
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,41 +1,48 @@
 | 
			
		|||
package handlers
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/gorilla/mux"
 | 
			
		||||
	"github.com/gorilla/sessions"
 | 
			
		||||
	"github.com/statping/statping/types/core"
 | 
			
		||||
	"github.com/statping/statping/types/null"
 | 
			
		||||
	"github.com/statping/statping/types/users"
 | 
			
		||||
	"github.com/statping/statping/utils"
 | 
			
		||||
	"golang.org/x/oauth2"
 | 
			
		||||
	"golang.org/x/oauth2/github"
 | 
			
		||||
	"golang.org/x/oauth2/google"
 | 
			
		||||
	"golang.org/x/oauth2/slack"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var oauthSession = sessions.NewCookieStore([]byte("statping_oauth"))
 | 
			
		||||
 | 
			
		||||
type oAuth struct {
 | 
			
		||||
	ID           string
 | 
			
		||||
	Email        string
 | 
			
		||||
	Username     string
 | 
			
		||||
	Token        string
 | 
			
		||||
	RefreshToken string
 | 
			
		||||
	Valid        bool
 | 
			
		||||
	Type         string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func oauthHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	vars := mux.Vars(r)
 | 
			
		||||
	provider := vars["provider"]
 | 
			
		||||
	code := r.URL.Query().Get("code")
 | 
			
		||||
	fmt.Println("code: ", code)
 | 
			
		||||
	fmt.Println("client: ", core.App.OAuth.SlackClientID)
 | 
			
		||||
	fmt.Println("secret: ", core.App.OAuth.SlackClientSecret)
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	var oauth *oAuth
 | 
			
		||||
	switch provider {
 | 
			
		||||
	case "google":
 | 
			
		||||
		err, oauth = googleOAuth(r)
 | 
			
		||||
		oauth, err = googleOAuth(r)
 | 
			
		||||
	case "github":
 | 
			
		||||
		err, oauth = githubOAuth(r)
 | 
			
		||||
		oauth, err = githubOAuth(r)
 | 
			
		||||
	case "slack":
 | 
			
		||||
		err, oauth = slackOAuth(r)
 | 
			
		||||
		oauth, err = slackOAuth(r)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -51,17 +58,18 @@ func oauthLogin(oauth *oAuth, w http.ResponseWriter, r *http.Request) {
 | 
			
		|||
	log.Infoln(oauth)
 | 
			
		||||
	user := &users.User{
 | 
			
		||||
		Id:       0,
 | 
			
		||||
		Username: oauth.Email,
 | 
			
		||||
		Username: oauth.Username,
 | 
			
		||||
		Email:    oauth.Email,
 | 
			
		||||
		Admin:    null.NewNullBool(true),
 | 
			
		||||
	}
 | 
			
		||||
	log.Infoln(fmt.Sprintf("OAuth User %v logged in from IP %v", oauth.Email, r.RemoteAddr))
 | 
			
		||||
	log.Infoln(fmt.Sprintf("OAuth User %s logged in from IP %s", oauth.Email, r.RemoteAddr))
 | 
			
		||||
	setJwtToken(user, w)
 | 
			
		||||
 | 
			
		||||
	http.Redirect(w, r, basePath+"dashboard", http.StatusSeeOther)
 | 
			
		||||
	//returnJson(user, w, r)
 | 
			
		||||
	http.Redirect(w, r, core.App.Domain, http.StatusPermanentRedirect)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func githubOAuth(r *http.Request) (error, *oAuth) {
 | 
			
		||||
func githubOAuth(r *http.Request) (*oAuth, error) {
 | 
			
		||||
	c := *core.App
 | 
			
		||||
	code := r.URL.Query().Get("code")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -73,58 +81,94 @@ func githubOAuth(r *http.Request) (error, *oAuth) {
 | 
			
		|||
 | 
			
		||||
	gg, err := config.Exchange(r.Context(), code)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err, nil
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, &oAuth{
 | 
			
		||||
	return &oAuth{
 | 
			
		||||
		Token:        gg.AccessToken,
 | 
			
		||||
		RefreshToken: gg.RefreshToken,
 | 
			
		||||
		Valid:        gg.Valid(),
 | 
			
		||||
	}
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func googleOAuth(r *http.Request) (error, *oAuth) {
 | 
			
		||||
func googleOAuth(r *http.Request) (*oAuth, error) {
 | 
			
		||||
	c := core.App
 | 
			
		||||
	code := r.URL.Query().Get("code")
 | 
			
		||||
 | 
			
		||||
	config := &oauth2.Config{
 | 
			
		||||
		ClientID:     c.OAuth.GithubClientID,
 | 
			
		||||
		ClientSecret: c.OAuth.GithubClientSecret,
 | 
			
		||||
		ClientID:     c.OAuth.GoogleClientID,
 | 
			
		||||
		ClientSecret: c.OAuth.GoogleClientSecret,
 | 
			
		||||
		Endpoint:     google.Endpoint,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	gg, err := config.Exchange(r.Context(), code)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err, nil
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, &oAuth{
 | 
			
		||||
	return &oAuth{
 | 
			
		||||
		Token:        gg.AccessToken,
 | 
			
		||||
		RefreshToken: gg.RefreshToken,
 | 
			
		||||
		Valid:        gg.Valid(),
 | 
			
		||||
	}
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func slackOAuth(r *http.Request) (error, *oAuth) {
 | 
			
		||||
	c := *core.App
 | 
			
		||||
func slackOAuth(r *http.Request) (*oAuth, error) {
 | 
			
		||||
	c := core.App
 | 
			
		||||
	code := r.URL.Query().Get("code")
 | 
			
		||||
 | 
			
		||||
	config := &oauth2.Config{
 | 
			
		||||
		ClientID:     c.OAuth.GithubClientID,
 | 
			
		||||
		ClientSecret: c.OAuth.GithubClientSecret,
 | 
			
		||||
		ClientID:     c.OAuth.SlackClientID,
 | 
			
		||||
		ClientSecret: c.OAuth.SlackClientSecret,
 | 
			
		||||
		Endpoint:     slack.Endpoint,
 | 
			
		||||
		RedirectURL:  c.Domain + basePath + "oauth/slack",
 | 
			
		||||
		Scopes:       []string{"identity.basic"},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	gg, err := config.Exchange(r.Context(), code)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err, nil
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, &oAuth{
 | 
			
		||||
	oauther := &oAuth{
 | 
			
		||||
		Token:        gg.AccessToken,
 | 
			
		||||
		RefreshToken: gg.RefreshToken,
 | 
			
		||||
		Valid:        gg.Valid(),
 | 
			
		||||
		Type:         gg.Type(),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return oauther.slackIdentity()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// slackIdentity will query the Slack API to fetch the users ID, username, and email address.
 | 
			
		||||
func (a *oAuth) slackIdentity() (*oAuth, error) {
 | 
			
		||||
	url := fmt.Sprintf("https://slack.com/api/users.identity?token=%s", a.Token)
 | 
			
		||||
	out, resp, err := utils.HttpRequest(url, "GET", "application/x-www-form-urlencoded", nil, nil, 10*time.Second, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return a, err
 | 
			
		||||
	}
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
 | 
			
		||||
	var i *slackIdentity
 | 
			
		||||
	if err := json.Unmarshal(out, &i); err != nil {
 | 
			
		||||
		return a, err
 | 
			
		||||
	}
 | 
			
		||||
	a.Email = i.User.Email
 | 
			
		||||
	a.ID = i.User.ID
 | 
			
		||||
	a.Username = i.User.Name
 | 
			
		||||
	return a, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type slackIdentity struct {
 | 
			
		||||
	Ok   bool `json:"ok"`
 | 
			
		||||
	User struct {
 | 
			
		||||
		Name  string `json:"name"`
 | 
			
		||||
		ID    string `json:"id"`
 | 
			
		||||
		Email string `json:"email"`
 | 
			
		||||
	} `json:"user"`
 | 
			
		||||
	Team struct {
 | 
			
		||||
		ID string `json:"id"`
 | 
			
		||||
	} `json:"team"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func secureToken(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,6 +24,7 @@ func staticAssets(src string) http.Handler {
 | 
			
		|||
func Router() *mux.Router {
 | 
			
		||||
	dir := utils.Directory
 | 
			
		||||
	CacheStorage = NewStorage()
 | 
			
		||||
 | 
			
		||||
	r := mux.NewRouter().StrictSlash(true)
 | 
			
		||||
 | 
			
		||||
	authUser := utils.Params.GetString("AUTH_USERNAME")
 | 
			
		||||
| 
						 | 
				
			
			@ -74,16 +75,19 @@ func Router() *mux.Router {
 | 
			
		|||
	r.Handle("/api", scoped(apiIndexHandler))
 | 
			
		||||
	r.Handle("/api/setup", http.HandlerFunc(processSetupHandler)).Methods("POST")
 | 
			
		||||
	api.Handle("/api/login", http.HandlerFunc(apiLoginHandler)).Methods("POST")
 | 
			
		||||
	api.Handle("/api/logout", http.HandlerFunc(logoutHandler))
 | 
			
		||||
	api.Handle("/api/logout", authenticated(logoutHandler, false))
 | 
			
		||||
	api.Handle("/api/renew", authenticated(apiRenewHandler, false))
 | 
			
		||||
	api.Handle("/api/cache", authenticated(apiCacheHandler, false)).Methods("GET")
 | 
			
		||||
	api.Handle("/api/clear_cache", authenticated(apiClearCacheHandler, false))
 | 
			
		||||
	api.Handle("/api/core", authenticated(apiCoreHandler, false)).Methods("POST")
 | 
			
		||||
	api.Handle("/api/oauth", scoped(apiOAuthHandler)).Methods("GET")
 | 
			
		||||
	api.Handle("/oauth/{provider}", http.HandlerFunc(oauthHandler))
 | 
			
		||||
	api.Handle("/api/logs", authenticated(logsHandler, false)).Methods("GET")
 | 
			
		||||
	api.Handle("/api/logs/last", authenticated(logsLineHandler, false)).Methods("GET")
 | 
			
		||||
 | 
			
		||||
	// API OAUTH Routes
 | 
			
		||||
	api.Handle("/api/oauth", scoped(apiOAuthHandler)).Methods("GET")
 | 
			
		||||
	api.Handle("/api/oauth", authenticated(apiUpdateOAuthHandler, false)).Methods("POST")
 | 
			
		||||
	api.Handle("/oauth/{provider}", http.HandlerFunc(oauthHandler))
 | 
			
		||||
 | 
			
		||||
	// API SCSS and ASSETS Routes
 | 
			
		||||
	api.Handle("/api/theme", authenticated(apiThemeViewHandler, false)).Methods("GET")
 | 
			
		||||
	api.Handle("/api/theme", authenticated(apiThemeSaveHandler, false)).Methods("POST")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,8 +31,6 @@ type Core struct {
 | 
			
		|||
	Setup         bool            `gorm:"-" json:"setup"`
 | 
			
		||||
	MigrationId   int64           `gorm:"column:migration_id" json:"migration_id,omitempty"`
 | 
			
		||||
	UseCdn        null.NullBool   `gorm:"column:use_cdn;default:false" json:"using_cdn,omitempty"`
 | 
			
		||||
	LoggedIn      bool            `gorm:"-" json:"logged_in"`
 | 
			
		||||
	IsAdmin       bool            `gorm:"-" json:"admin"`
 | 
			
		||||
	AllowReports  null.NullBool   `gorm:"column:allow_reports;default:false" json:"allow_reports,omitempty"`
 | 
			
		||||
	CreatedAt     time.Time       `gorm:"column:created_at" json:"created_at"`
 | 
			
		||||
	UpdatedAt     time.Time       `gorm:"column:updated_at" json:"updated_at"`
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,4 +16,5 @@ type User struct {
 | 
			
		|||
	Admin     null.NullBool `gorm:"column:administrator" json:"admin,omitempty"`
 | 
			
		||||
	CreatedAt time.Time     `gorm:"column:created_at" json:"created_at"`
 | 
			
		||||
	UpdatedAt time.Time     `gorm:"column:updated_at" json:"updated_at"`
 | 
			
		||||
	Token     string        `gorm:"-" json:"token"`
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue