mirror of https://github.com/statping/statping
commit
cbc394a39f
|
@ -1,3 +1,6 @@
|
|||
# 0.90.57 (07-04-2020)
|
||||
- Fixed login issue
|
||||
|
||||
# 0.90.56 (06-25-2020)
|
||||
- Modified metrics now include service name for each service metric
|
||||
- Added switch for true/false notifier values
|
||||
|
|
|
@ -398,12 +398,17 @@ func ExportSettings() ([]byte, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var srvs []services.Service
|
||||
for _, s := range services.AllInOrder() {
|
||||
s.Failures = nil
|
||||
srvs = append(srvs, s)
|
||||
}
|
||||
data := ExportData{
|
||||
Core: c,
|
||||
//Notifiers: notifications.All(),
|
||||
Notifiers: core.App.Notifications,
|
||||
Checkins: checkins.All(),
|
||||
Users: users.All(),
|
||||
Services: services.AllInOrder(),
|
||||
Services: srvs,
|
||||
Groups: groups.All(),
|
||||
Messages: messages.All(),
|
||||
}
|
||||
|
|
|
@ -3,13 +3,14 @@ import axios from 'axios'
|
|||
import * as Sentry from "@sentry/browser";
|
||||
import * as Integrations from "@sentry/integrations";
|
||||
const qs = require('querystring');
|
||||
axios.defaults.withCredentials = true
|
||||
|
||||
const tokenKey = "statping_auth";
|
||||
const errorReporter = "https://bed4d75404924cb3a799e370733a1b64@sentry.statping.com/3"
|
||||
|
||||
class Api {
|
||||
constructor() {
|
||||
axios.defaults.withCredentials = true
|
||||
|
||||
}
|
||||
|
||||
async oauth() {
|
||||
|
|
|
@ -689,7 +689,7 @@ HTML,BODY {
|
|||
transition: 0.2s all;
|
||||
}
|
||||
.switch-rd-gr input:checked + label::before {
|
||||
background-color: #cd141b;
|
||||
background-color: #29b10c !important;
|
||||
}
|
||||
.switch input:checked + label::before {
|
||||
background-color: #08d;
|
||||
|
|
|
@ -4,18 +4,42 @@
|
|||
<div class="card contain-card text-black-50 bg-white mb-3">
|
||||
<div class="card-header text-capitalize">
|
||||
{{notifier.title}}
|
||||
<span @click="enableToggle" class="switch switch-rd-gr float-right">
|
||||
<input v-model="notifier.enabled" type="checkbox" class="switch-sm" :id="`enable_${notifier.method}`" v-bind:checked="notifier.enabled">
|
||||
<span @click="enableToggle" class="switch switch-sm switch-rd-gr float-right">
|
||||
<input v-model="notifier.enabled" type="checkbox" :id="`enable_${notifier.method}`" v-bind:checked="notifier.enabled">
|
||||
<label class="mb-0" :for="`enable_${notifier.method}`"></label>
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="small text-muted" v-html="notifier.description"/>
|
||||
|
||||
<div v-if="notifier.method==='mobile'" class="col-6 offset-3">
|
||||
<div v-if="notifier.method==='mobile'">
|
||||
<div class="form-group row mt-3">
|
||||
<label for="domain" class="col-sm-4 col-form-label">Statping Domain</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<input v-bind:value="$store.getters.core.domain" type="text" class="form-control" id="domain" readonly>
|
||||
<div class="input-group-append copy-btn">
|
||||
<button @click.prevent="copy($store.getters.core.domain)" class="btn btn-outline-secondary" type="button">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row mt-3">
|
||||
<label for="apisecret" class="col-sm-4 col-form-label">API Secret</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<input v-bind:value="$store.getters.core.api_secret" type="text" class="form-control" id="apisecret" readonly>
|
||||
<div class="input-group-append copy-btn">
|
||||
<button @click.prevent="copy($store.getters.core.api_secret)" class="btn btn-outline-secondary" type="button">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 offset-0 offset-md-3">
|
||||
<img :src="qrcode" class="img-thumbnail">
|
||||
<span class="text-muted small center">Scan this QR Code on the Statping Mobile App for quick setup</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="notifier.method!=='mobile'" v-for="(form, index) in notifier.form" v-bind:key="index" class="form-group">
|
||||
<label class="text-capitalize">{{form.title}}</label>
|
||||
|
@ -47,10 +71,11 @@
|
|||
|
||||
<div v-if="notifier.data_type" class="card text-black-50 bg-white mb-3">
|
||||
<div class="card-header text-capitalize">
|
||||
<font-awesome-icon @click="expanded = !expanded" :icon="expanded ? 'minus' : 'plus'" class="mr-2 pointer"/>
|
||||
{{notifier.title}} Outgoing Request
|
||||
<span class="badge badge-dark float-right text-uppercase mt-1">{{notifier.data_type}}</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="card-body" :class="{'d-none': !expanded}">
|
||||
<span class="text-muted d-block mb-3" v-if="notifier.request_info" v-html="notifier.request_info"></span>
|
||||
|
||||
<div class="row" v-observe-visibility="visible">
|
||||
|
@ -97,16 +122,16 @@
|
|||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-4 col-sm-4 mb-2 mb-sm-0 mt-2 mt-sm-0">
|
||||
<button @click.prevent="saveNotifier" type="submit" class="btn btn-block text-capitalize btn-primary save-notifier">
|
||||
<i class="fa fa-check-circle"></i> {{loading ? "Loading..." : saved ? "Saved" : "Save Settings"}}
|
||||
<div class="col-12 col-sm-4 mb-2 mb-sm-0 mt-2 mt-sm-0">
|
||||
<button @click.prevent="saveNotifier" :disabled="loading" type="submit" class="btn btn-block text-capitalize btn-primary save-notifier">
|
||||
<font-awesome-icon v-if="loading" icon="circle-notch" class="mr-2" spin/> {{loading ? "Loading..." : saved ? "Saved" : "Save"}}
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-4 col-md-4">
|
||||
<div class="col-12 col-md-4 mb-2 mb-sm-0 mt-2 mt-sm-0">
|
||||
<button @click.prevent="testNotifier('success')" :disabled="loadingTest" class="btn btn-outline-dark btn-block text-capitalize test-notifier">
|
||||
<font-awesome-icon v-if="loadingTest" icon="circle-notch" class="mr-2" spin/>{{loadingTest ? "Loading..." : "Test Success"}}</button>
|
||||
</div>
|
||||
<div class="col-4 col-md-4">
|
||||
<div class="col-12 col-md-4 mb-2 mb-sm-0 mt-2 mt-sm-0">
|
||||
<button @click.prevent="testNotifier('failure')" :disabled="loadingTest" class="btn btn-outline-dark btn-block text-capitalize test-notifier">
|
||||
<font-awesome-icon v-if="loadingTest" icon="circle-notch" class="mr-2" spin/>{{loadingTest ? "Loading..." : "Test Failure"}}</button>
|
||||
</div>
|
||||
|
@ -157,6 +182,7 @@ export default {
|
|||
request: null,
|
||||
success: false,
|
||||
saved: false,
|
||||
expanded: false,
|
||||
success_data: null,
|
||||
failure_data: null,
|
||||
form: {},
|
||||
|
@ -226,6 +252,7 @@ export default {
|
|||
this.form.enabled = this.notifier.enabled
|
||||
this.form.limits = parseInt(this.notifier.limits)
|
||||
this.form.method = this.notifier.method
|
||||
if (this.notifier.form) {
|
||||
this.notifier.form.forEach((f) => {
|
||||
let field = f.field.toLowerCase()
|
||||
let val = this.notifier[field]
|
||||
|
@ -234,6 +261,7 @@ export default {
|
|||
}
|
||||
this.form[field] = val
|
||||
});
|
||||
}
|
||||
this.form.success_data = this.success_data
|
||||
this.form.failure_data = this.failure_data
|
||||
await Api.notifier_save(this.form)
|
||||
|
@ -246,6 +274,7 @@ export default {
|
|||
this.success = false
|
||||
this.loadingTest = true
|
||||
this.form.method = this.notifier.method
|
||||
if (this.notifier.form) {
|
||||
this.notifier.form.forEach((f) => {
|
||||
let field = f.field.toLowerCase()
|
||||
let val = this.notifier[field]
|
||||
|
@ -254,6 +283,7 @@ export default {
|
|||
}
|
||||
this.form[field] = val
|
||||
});
|
||||
}
|
||||
let req = {
|
||||
notifier: this.form,
|
||||
method: method,
|
||||
|
|
|
@ -1,23 +1,27 @@
|
|||
<template>
|
||||
<form @submit.prevent="saveOAuth">
|
||||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card-header">Internal Login</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
<label for="switch-gh-oauth" class="col-sm-6 col-form-label">Statping Authentication</label>
|
||||
<div class="col-md-6 col-xs-12 mt-1">
|
||||
<span @click="local_enabled = !!local_enabled" class="switch float-left">
|
||||
<input v-model="local_enabled" type="checkbox" class="switch" id="switch-local-oauth" :checked="local_enabled">
|
||||
<label for="switch-local-oauth"></label>
|
||||
<span class="small d-block">Use email/password Authentication</span>
|
||||
<div class="card-header">
|
||||
Internal Login
|
||||
<span @click="local_enabled = !!local_enabled" class="switch switch-sm switch-rd-gr float-right">
|
||||
<input v-model="local_enabled" type="checkbox" id="switch-internal-oauth" :checked="local_enabled">
|
||||
<label for="switch-internal-oauth" class="mb-0"> </label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
Use Statping's default authentication to allow users you've created to login.
|
||||
</div>
|
||||
</div>
|
||||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card-header">Github Settings</div>
|
||||
<div class="card-body">
|
||||
<div class="card-header text-capitalize">
|
||||
<font-awesome-icon @click="expanded.github = !expanded.github" :icon="expanded.github ? 'minus' : 'plus'" class="mr-2 pointer"/>
|
||||
Github Settings
|
||||
<span @click="github_enabled = !!github_enabled" class="switch switch-sm switch-rd-gr float-right">
|
||||
<input v-model="github_enabled" type="checkbox" id="switch-gh-oauth" :checked="github_enabled">
|
||||
<label class="mb-0" for="switch-gh-oauth"> </label>
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-body" :class="{'d-none': !expanded.github}">
|
||||
<span>You will need to create a new <a href="https://github.com/settings/developers">OAuth App</a> within Github.</span>
|
||||
|
||||
<div class="form-group row mt-3">
|
||||
|
@ -47,12 +51,9 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="switch-gh-oauth" class="col-sm-4 col-form-label">Enable Github Login</label>
|
||||
<label class="col-sm-4 col-form-label">Enable Github Login</label>
|
||||
<div class="col-md-8 col-xs-12 mt-1">
|
||||
<span @click="github_enabled = !!github_enabled" class="switch float-left">
|
||||
<input v-model="github_enabled" type="checkbox" class="switch" id="switch-gh-oauth" :checked="github_enabled">
|
||||
<label for="switch-gh-oauth"> </label>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
|
@ -69,8 +70,15 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card-header">Google Settings</div>
|
||||
<div class="card-body">
|
||||
<div class="card-header">
|
||||
<font-awesome-icon @click="expanded.google = !expanded.google" :icon="expanded.google ? 'minus' : 'plus'" class="mr-2 pointer"/>
|
||||
Google Settings
|
||||
<span @click="google_enabled = !!google_enabled" class="switch switch-sm switch-rd-gr float-right">
|
||||
<input v-model="google_enabled" type="checkbox" id="switch-google-oauth" :checked="google_enabled">
|
||||
<label for="switch-google-oauth" class="mb-0"> </label>
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-body" :class="{'d-none': !expanded.google}">
|
||||
<span>Go to <a href="https://console.cloud.google.com/apis/credentials">OAuth Consent Screen</a> on Google Console to create a new "Web Application" OAuth application. </span>
|
||||
|
||||
<div class="form-group row mt-3">
|
||||
|
@ -92,15 +100,6 @@
|
|||
<small>Optional comma delimited list of emails and/or domains</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="switch-google-oauth" class="col-sm-4 col-form-label">Enable Google Login</label>
|
||||
<div class="col-md-8 col-xs-12 mt-1">
|
||||
<span @click="google_enabled = !!google_enabled" class="switch float-left">
|
||||
<input v-model="google_enabled" type="checkbox" class="switch" id="switch-google-oauth" :checked="google_enabled">
|
||||
<label for="switch-google-oauth"> </label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="google_callback" class="col-sm-4 col-form-label">Callback URL</label>
|
||||
<div class="col-sm-8">
|
||||
|
@ -115,8 +114,15 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card-header">Slack Settings</div>
|
||||
<div class="card-body">
|
||||
<div class="card-header">
|
||||
<font-awesome-icon @click="expanded.slack = !expanded.slack" :icon="expanded.slack ? 'minus' : 'plus'" class="mr-2 pointer"/>
|
||||
Slack Settings
|
||||
<span @click="slack_enabled = !!slack_enabled" class="switch switch-sm switch-rd-gr float-right">
|
||||
<input v-model="slack_enabled" type="checkbox" id="switch-slack-oauth" :checked="slack_enabled">
|
||||
<label for="switch-slack-oauth" class="mb-0"> </label>
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-body" :class="{'d-none': !expanded.slack}">
|
||||
<span>Go to <a href="https://api.slack.com/apps">Slack Apps</a> and create a new Application.</span>
|
||||
|
||||
<div class="form-group row mt-3">
|
||||
|
@ -145,15 +151,6 @@
|
|||
<small>Optional comma delimited list of email addresses</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="switch-slack-oauth" class="col-sm-4 col-form-label">Enable Slack Login</label>
|
||||
<div class="col-md-8 col-xs-12 mt-1">
|
||||
<span @click="slack_enabled = !!slack_enabled" class="switch float-left">
|
||||
<input v-model="slack_enabled" type="checkbox" class="switch" id="switch-slack-oauth" :checked="slack_enabled">
|
||||
<label for="switch-slack-oauth"> </label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="slack_callback" class="col-sm-4 col-form-label">Callback URL</label>
|
||||
<div class="col-sm-8">
|
||||
|
@ -169,8 +166,15 @@
|
|||
</div>
|
||||
|
||||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card-header">Custom oAuth Settings</div>
|
||||
<div class="card-body">
|
||||
<div class="card-header">
|
||||
<font-awesome-icon @click="expanded.custom = !expanded.custom" :icon="expanded.custom ? 'minus' : 'plus'" class="mr-2 pointer"/>
|
||||
Custom oAuth Settings
|
||||
<span @click="custom_enabled = !!custom_enabled" class="switch switch-sm switch-rd-gr float-right">
|
||||
<input v-model="custom_enabled" type="checkbox" id="switch-custom-oauth" :checked="custom_enabled">
|
||||
<label for="switch-custom-oauth" class="mb-0"> </label>
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-body" :class="{'d-none': !expanded.custom}">
|
||||
<div class="form-group row mt-3">
|
||||
<label for="custom_name" class="col-sm-4 col-form-label">Custom Name</label>
|
||||
<div class="col-sm-8">
|
||||
|
@ -208,15 +212,6 @@
|
|||
<small>Optional comma delimited list of oauth scopes</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="switch-custom-oauth" class="col-sm-4 col-form-label">Enable Custom Login</label>
|
||||
<div class="col-md-8 col-xs-12 mt-1">
|
||||
<span @click="custom_enabled = !!custom_enabled" class="switch float-left">
|
||||
<input v-model="custom_enabled" type="checkbox" class="switch" id="switch-custom-oauth" :checked="custom_enabled">
|
||||
<label for="switch-custom-oauth"> </label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="slack_callback" class="col-sm-4 col-form-label">Callback URL</label>
|
||||
<div class="col-sm-8">
|
||||
|
@ -231,7 +226,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<button class="btn btn-primary btn-block" @click.prevent="saveOAuth" type="submit" :disabled="loading">
|
||||
<font-awesome-icon v-if="loading" icon="circle-notch" class="mr-2" spin/> Save OAuth Settings
|
||||
</button>
|
||||
|
@ -257,6 +251,13 @@
|
|||
local_enabled: false,
|
||||
custom_enabled: false,
|
||||
loading: false,
|
||||
expanded: {
|
||||
github: false,
|
||||
google: false,
|
||||
slack: false,
|
||||
custom: false,
|
||||
openid: false,
|
||||
},
|
||||
oauth: {
|
||||
gh_client_id: "",
|
||||
gh_client_secret: "",
|
||||
|
|
|
@ -32,11 +32,22 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">{{ $t('close') }}</label>
|
||||
<label class="col-sm-4 col-form-label">Confirm Password</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="user.confirm_password" type="password" id="password_confirm" class="form-control" placeholder="Confirm Password" required>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="user.api_key" class="form-group row">
|
||||
<label for="user_key_key" class="col-sm-4 col-form-label">API Key</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<input v-bind:value="user.api_key" type="text" class="form-control" id="user_key_key" readonly>
|
||||
<div class="input-group-append copy-btn">
|
||||
<button @click.prevent="copy(user.api_key)" class="btn btn-outline-secondary" type="button">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-12">
|
||||
<LoadButton
|
||||
|
@ -76,7 +87,8 @@
|
|||
admin: false,
|
||||
email: "",
|
||||
password: "",
|
||||
confirm_password: ""
|
||||
confirm_password: "",
|
||||
api_key: "",
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<font-awesome-icon icon="paperclip" class="mr-2"/> {{ $t('settings.cache') }}
|
||||
</a>
|
||||
<a @click.prevent="changeTab" class="nav-link" v-bind:class="{active: liClass('v-pills-oauth-tab')}" id="v-pills-oauth-tab" data-toggle="pill" href="#v-pills-oauth" role="tab" aria-controls="v-pills-oauth" aria-selected="false">
|
||||
<font-awesome-icon icon="key" class="mr-2"/> {{ $t('settings.oauth') }} <span class="mt-1 float-right badge badge-light text-dark font-1">{{ $t('settings.beta') }}</span>
|
||||
<font-awesome-icon icon="key" class="mr-2"/> {{ $t('settings.oauth') }}
|
||||
</a>
|
||||
|
||||
<h6 class="mt-4 text-muted">Notifiers</h6>
|
||||
|
@ -86,13 +86,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card text-black-50 bg-white mt-3">
|
||||
<div class="card-header">QR Code for Mobile App</div>
|
||||
<div class="card-body">
|
||||
<img class="rounded" width="300" height="300" :src="qrcode">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" v-bind:class="{active: liClass('v-pills-style-tab'), show: liClass('v-pills-style-tab')}" id="v-pills-style" role="tabpanel" aria-labelledby="v-pills-style-tab">
|
||||
|
@ -149,7 +142,6 @@
|
|||
data() {
|
||||
return {
|
||||
tab: "v-pills-home-tab",
|
||||
qrcode: "",
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -172,9 +164,6 @@
|
|||
this.$store.commit('setCore', c)
|
||||
const n = await Api.notifiers()
|
||||
this.$store.commit('setNotifiers', n)
|
||||
|
||||
const u = `statping://setup?domain=${c.domain}&api=${c.api_secret}`
|
||||
this.qrcode = "https://chart.googleapis.com/chart?chs=500x500&cht=qr&chl=" + encodeURIComponent(u)
|
||||
this.cache = await Api.cache()
|
||||
},
|
||||
changeTab(e) {
|
||||
|
|
|
@ -264,7 +264,7 @@ func TestMainApiRoutes(t *testing.T) {
|
|||
|
||||
type HttpFuncTest func(*testing.T) error
|
||||
|
||||
type ResponseFunc func(*testing.T, []byte) error
|
||||
type ResponseFunc func(*httptest.ResponseRecorder, *testing.T, []byte) error
|
||||
|
||||
// HTTPTest contains all the parameters for a HTTP Unit Test
|
||||
type HTTPTest struct {
|
||||
|
@ -350,7 +350,7 @@ func RunHTTPTest(test HTTPTest, t *testing.T) (string, *testing.T, error) {
|
|||
assert.Nil(t, err)
|
||||
}
|
||||
if test.ResponseFunc != nil {
|
||||
err := test.ResponseFunc(t, body)
|
||||
err := test.ResponseFunc(rr, t, body)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package handlers
|
|||
import (
|
||||
"crypto/subtle"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/users"
|
||||
"github.com/statping/statping/utils"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
@ -33,6 +34,14 @@ func hasAPIQuery(r *http.Request) bool {
|
|||
if subtle.ConstantTimeCompare([]byte(key), []byte(core.App.ApiSecret)) == 1 {
|
||||
return true
|
||||
}
|
||||
// find user with API key
|
||||
user, err := users.FindByAPIKey(key)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if subtle.ConstantTimeCompare([]byte(key), []byte(user.ApiKey)) == 1 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
type JwtClaim struct {
|
||||
Username string `json:"username"`
|
||||
Admin bool `json:"admin"`
|
||||
Scopes string `json:"scopes"`
|
||||
jwt.StandardClaims
|
||||
}
|
||||
|
||||
|
@ -29,6 +30,7 @@ func setJwtToken(user *users.User, w http.ResponseWriter) (JwtClaim, string) {
|
|||
jwtClaim := JwtClaim{
|
||||
Username: user.Username,
|
||||
Admin: user.Admin.Bool,
|
||||
Scopes: user.Scopes,
|
||||
StandardClaims: jwt.StandardClaims{
|
||||
ExpiresAt: expirationTime.Unix(),
|
||||
}}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/statping/statping/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
@ -214,7 +215,7 @@ func TestApiServiceRoutes(t *testing.T) {
|
|||
URL: "/api/services/1/uptime_data" + startEndQuery,
|
||||
Method: "GET",
|
||||
ExpectedStatus: 200,
|
||||
ResponseFunc: func(t *testing.T, resp []byte) error {
|
||||
ResponseFunc: func(req *httptest.ResponseRecorder, t *testing.T, resp []byte) error {
|
||||
var uptime *services.UptimeSeries
|
||||
if err := json.Unmarshal(resp, &uptime); err != nil {
|
||||
return err
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package notifiers
|
||||
|
||||
import (
|
||||
"github.com/statping/statping/types/errors"
|
||||
"github.com/statping/statping/types/failures"
|
||||
"github.com/statping/statping/types/notifications"
|
||||
"github.com/statping/statping/types/notifier"
|
||||
|
@ -28,44 +29,44 @@ var Command = &commandLine{¬ifications.Notification{
|
|||
AuthorUrl: "https://github.com/hunterlong",
|
||||
Delay: time.Duration(1 * time.Second),
|
||||
Icon: "fas fa-terminal",
|
||||
Host: "/bin/bash",
|
||||
SuccessData: "curl -L http://localhost:8080",
|
||||
FailureData: "curl -L http://localhost:8080",
|
||||
SuccessData: "/usr/bin/curl -L http://localhost:8080",
|
||||
FailureData: "/usr/bin/curl -L http://localhost:8080",
|
||||
DataType: "text",
|
||||
Limits: 60,
|
||||
Form: []notifications.NotificationForm{{
|
||||
Type: "text",
|
||||
Title: "Executable Path",
|
||||
Placeholder: "/usr/bin/curl",
|
||||
DbField: "host",
|
||||
SmallText: "You can use '/bin/sh', '/bin/bash', '/usr/bin/curl' or an absolute path for an application.",
|
||||
}}},
|
||||
}
|
||||
}}
|
||||
|
||||
func runCommand(app string, cmd ...string) (string, string, error) {
|
||||
utils.Log.Infof("Command notifier sending: %s %s", app, strings.Join(cmd, " "))
|
||||
outStr, errStr, err := utils.Command(app, cmd...)
|
||||
func runCommand(cmd string) (string, string, error) {
|
||||
utils.Log.Infof("Command notifier sending: %s", cmd)
|
||||
cmdApp := strings.Split(cmd, " ")
|
||||
if len(cmd) == 0 {
|
||||
return "", "", errors.New("you need at least 1 command")
|
||||
}
|
||||
var cmdArgs []string
|
||||
if len(cmd) > 1 {
|
||||
cmdArgs = append(cmdArgs, cmd[1:])
|
||||
}
|
||||
outStr, errStr, err := utils.Command(cmdApp[0], cmdArgs...)
|
||||
return outStr, errStr, err
|
||||
}
|
||||
|
||||
// OnSuccess for commandLine will trigger successful service
|
||||
func (c *commandLine) OnSuccess(s services.Service) (string, error) {
|
||||
tmpl := ReplaceVars(c.SuccessData, s, failures.Failure{})
|
||||
out, _, err := runCommand(c.Host, tmpl)
|
||||
out, _, err := runCommand(tmpl)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// OnFailure for commandLine will trigger failing service
|
||||
func (c *commandLine) OnFailure(s services.Service, f failures.Failure) (string, error) {
|
||||
tmpl := ReplaceVars(c.FailureData, s, f)
|
||||
_, ouerr, err := runCommand(c.Host, tmpl)
|
||||
return ouerr, err
|
||||
out, _, err := runCommand(tmpl)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// OnTest for commandLine triggers when this notifier has been saved
|
||||
func (c *commandLine) OnTest() (string, error) {
|
||||
tmpl := ReplaceVars(c.Var1, services.Example(true), failures.Example())
|
||||
in, out, err := runCommand(c.Host, tmpl)
|
||||
in, out, err := runCommand(tmpl)
|
||||
utils.Log.Infoln(in)
|
||||
utils.Log.Infoln(out)
|
||||
return out, err
|
||||
|
|
|
@ -39,7 +39,7 @@ var statpingMailer = &statpingEmailer{¬ifications.Notification{
|
|||
Form: []notifications.NotificationForm{{
|
||||
Type: "email",
|
||||
Title: "Send to Email Address",
|
||||
Placeholder: "Insert your email address",
|
||||
Placeholder: "info@statping.com",
|
||||
DbField: "Host",
|
||||
Required: true,
|
||||
}}},
|
||||
|
|
|
@ -88,6 +88,7 @@ func CreateAdminUser(c *DbConfig) error {
|
|||
Username: adminUser,
|
||||
Password: adminPass,
|
||||
Email: adminEmail,
|
||||
Scopes: "admin",
|
||||
Admin: null.NewNullBool(true),
|
||||
}
|
||||
|
||||
|
|
|
@ -47,22 +47,8 @@ func Select() (*Core, error) {
|
|||
}
|
||||
|
||||
func (c *Core) Create() error {
|
||||
secret := utils.Params.GetString("API_SECRET")
|
||||
if secret == "" {
|
||||
secret = utils.RandomString(32)
|
||||
}
|
||||
newCore := &Core{
|
||||
Name: c.Name,
|
||||
Description: c.Description,
|
||||
ConfigFile: utils.Directory + "/config.yml",
|
||||
ApiSecret: secret,
|
||||
Version: App.Version,
|
||||
Domain: c.Domain,
|
||||
Language: c.Language,
|
||||
MigrationId: utils.Now().Unix(),
|
||||
}
|
||||
q := db.Create(&newCore)
|
||||
utils.Log.Infof("API Key created: %s", secret)
|
||||
q := db.Create(c)
|
||||
utils.Log.Infof("API Key created: %s", c.ApiSecret)
|
||||
return q.Error()
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ func Samples() error {
|
|||
MigrationId: utils.Now().Unix(),
|
||||
Language: utils.Params.GetString("LANGUAGE"),
|
||||
OAuth: oauth,
|
||||
Version: utils.Version,
|
||||
}
|
||||
|
||||
return core.Create()
|
||||
|
|
|
@ -43,6 +43,12 @@ func FindByUsername(username string) (*User, error) {
|
|||
return &user, q.Error()
|
||||
}
|
||||
|
||||
func FindByAPIKey(key string) (*User, error) {
|
||||
var user User
|
||||
q := db.Where("api_key = ?", key).Find(&user)
|
||||
return &user, q.Error()
|
||||
}
|
||||
|
||||
func All() []*User {
|
||||
var users []*User
|
||||
db.Find(&users)
|
||||
|
|
|
@ -5,6 +5,5 @@ import "github.com/statping/statping/utils"
|
|||
func (u *User) BeforeCreate() error {
|
||||
u.Password = utils.HashPassword(u.Password)
|
||||
u.ApiKey = utils.NewSHA256Hash()
|
||||
u.ApiSecret = utils.NewSHA256Hash()
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ func Samples() error {
|
|||
Username: "testadmin",
|
||||
Password: "password123",
|
||||
Email: "info@betatude.com",
|
||||
Scopes: "admin",
|
||||
Admin: null.NewNullBool(true),
|
||||
}
|
||||
|
||||
|
@ -21,6 +22,7 @@ func Samples() error {
|
|||
Username: "testadmin2",
|
||||
Password: "password123",
|
||||
Email: "info@adminhere.com",
|
||||
Scopes: "admin",
|
||||
Admin: null.NewNullBool(true),
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package users
|
||||
|
||||
import "strings"
|
||||
|
||||
type Scope string
|
||||
|
||||
const (
|
||||
FullAdmin Scope = "admin"
|
||||
ReadOnly Scope = "readonly"
|
||||
RServices Scope = "read:services"
|
||||
RWServices Scope = "write:services"
|
||||
RIncidents Scope = "read:incidents"
|
||||
RWIncidents Scope = "write:incidents"
|
||||
|
||||
EmptyUser Scope = "none"
|
||||
)
|
||||
|
||||
func namedScope(name string) Scope {
|
||||
switch name {
|
||||
case "admin":
|
||||
return FullAdmin
|
||||
case "readonly":
|
||||
return ReadOnly
|
||||
case "read:services":
|
||||
return RServices
|
||||
case "write:services":
|
||||
return RWServices
|
||||
case "read:incidents":
|
||||
return RIncidents
|
||||
case "write:incidents":
|
||||
return RWIncidents
|
||||
default:
|
||||
return EmptyUser
|
||||
}
|
||||
}
|
||||
|
||||
func (u *User) AllScopes() []Scope {
|
||||
var scopes []Scope
|
||||
for _, s := range strings.Split(u.Scopes, ",") {
|
||||
scopes = append(scopes, namedScope(s))
|
||||
}
|
||||
return scopes
|
||||
}
|
|
@ -12,7 +12,7 @@ type User struct {
|
|||
Password string `gorm:"column:password" json:"password,omitempty"`
|
||||
Email string `gorm:"type:varchar(100);column:email" json:"email,omitempty"`
|
||||
ApiKey string `gorm:"column:api_key" json:"api_key,omitempty"`
|
||||
ApiSecret string `gorm:"column:api_secret" json:"api_secret,omitempty"`
|
||||
Scopes string `gorm:"column:scopes" json:"scopes,omitempty"`
|
||||
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"`
|
||||
|
|
|
@ -31,7 +31,6 @@ func TestFind(t *testing.T) {
|
|||
require.Nil(t, err)
|
||||
assert.Equal(t, "example_user", item.Username)
|
||||
assert.NotEmpty(t, item.ApiKey)
|
||||
assert.NotEmpty(t, item.ApiSecret)
|
||||
assert.NotEqual(t, "password123", item.Password)
|
||||
assert.True(t, item.Admin.Bool)
|
||||
}
|
||||
|
@ -41,7 +40,6 @@ func TestFindByUsername(t *testing.T) {
|
|||
require.Nil(t, err)
|
||||
assert.Equal(t, "example_user", item.Username)
|
||||
assert.NotEmpty(t, item.ApiKey)
|
||||
assert.NotEmpty(t, item.ApiSecret)
|
||||
assert.NotEqual(t, "password123", item.Password)
|
||||
assert.True(t, item.Admin.Bool)
|
||||
}
|
||||
|
@ -64,7 +62,6 @@ func TestCreate(t *testing.T) {
|
|||
assert.NotEqual(t, "password12345", example.Password)
|
||||
assert.NotZero(t, example.CreatedAt)
|
||||
assert.NotEmpty(t, example.ApiKey)
|
||||
assert.NotEmpty(t, example.ApiSecret)
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.90.56
|
||||
0.90.57
|
||||
|
|
Loading…
Reference in New Issue