Merge pull request #727 from statping/dev

Login Fix
pull/740/head^2
Hunter Long 2020-07-04 13:49:58 -07:00 committed by GitHub
commit cbc394a39f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 232 additions and 143 deletions

View File

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

View File

@ -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(),
}

View File

@ -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() {

View File

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

View File

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

View File

@ -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: "",

View File

@ -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: "",
}
}
},

View File

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

View File

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

View File

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

View File

@ -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(),
}}

View File

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

View File

@ -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{&notifications.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

View File

@ -39,7 +39,7 @@ var statpingMailer = &statpingEmailer{&notifications.Notification{
Form: []notifications.NotificationForm{{
Type: "email",
Title: "Send to Email Address",
Placeholder: "Insert your email address",
Placeholder: "info@statping.com",
DbField: "Host",
Required: true,
}}},

View File

@ -88,6 +88,7 @@ func CreateAdminUser(c *DbConfig) error {
Username: adminUser,
Password: adminPass,
Email: adminEmail,
Scopes: "admin",
Admin: null.NewNullBool(true),
}

View File

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

View File

@ -41,6 +41,7 @@ func Samples() error {
MigrationId: utils.Now().Unix(),
Language: utils.Params.GetString("LANGUAGE"),
OAuth: oauth,
Version: utils.Version,
}
return core.Create()

View File

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

View File

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

View File

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

43
types/users/scopes.go Normal file
View File

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

View File

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

View File

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

View File

@ -1 +1 @@
0.90.56
0.90.57