mirror of https://github.com/statping/statping
added github oauth username and organizations for authentication
parent
cbb80bb55a
commit
ea23865494
|
@ -319,7 +319,7 @@ jobs:
|
|||
dockerfile: Dockerfile.base
|
||||
tags: "base"
|
||||
|
||||
- name: Latest/Version Docker Image
|
||||
- name: Dev Docker Image
|
||||
uses: elgohr/Publish-Docker-Github-Action@master
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
|
|
|
@ -5,10 +5,9 @@ on:
|
|||
- '*' # matches every branch
|
||||
- '*/*' # matches every branch containing a single '/'
|
||||
- '!master' # excludes master
|
||||
- '!dev' # excludes dev
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- dev
|
||||
|
||||
jobs:
|
||||
compile:
|
||||
|
|
|
@ -46,6 +46,18 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="github_secret" class="col-sm-4 col-form-label">Github Restrict Users</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="oauth.gh_users" type="text" class="form-control" id="github_users">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="github_secret" class="col-sm-4 col-form-label">Github Restrict Organizations</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="oauth.gh_orgs" type="text" class="form-control" id="github_orgs">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="gh_callback" class="col-sm-4 col-form-label">Callback URL</label>
|
||||
<div class="col-sm-8">
|
||||
|
@ -172,6 +184,8 @@
|
|||
oauth: {
|
||||
gh_client_id: "",
|
||||
gh_client_secret: "",
|
||||
gh_users: "",
|
||||
gh_orgs: "",
|
||||
google_client_id: "",
|
||||
google_client_secret: "",
|
||||
oauth_domains: "",
|
||||
|
|
|
@ -1,18 +1,12 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
"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/google"
|
||||
"golang.org/x/oauth2/slack"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type oAuth struct {
|
||||
|
@ -63,87 +57,3 @@ func oauthLogin(oauth *oAuth, w http.ResponseWriter, r *http.Request) {
|
|||
//returnJson(user, w, r)
|
||||
http.Redirect(w, r, core.App.Domain+"/dashboard", http.StatusPermanentRedirect)
|
||||
}
|
||||
|
||||
func googleOAuth(r *http.Request) (*oAuth, error) {
|
||||
c := core.App
|
||||
code := r.URL.Query().Get("code")
|
||||
|
||||
config := &oauth2.Config{
|
||||
ClientID: c.OAuth.GoogleClientID,
|
||||
ClientSecret: c.OAuth.GoogleClientSecret,
|
||||
Endpoint: google.Endpoint,
|
||||
}
|
||||
|
||||
gg, err := config.Exchange(r.Context(), code)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &oAuth{
|
||||
Token: gg.AccessToken,
|
||||
RefreshToken: gg.RefreshToken,
|
||||
Valid: gg.Valid(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func slackOAuth(r *http.Request) (*oAuth, error) {
|
||||
c := core.App
|
||||
code := r.URL.Query().Get("code")
|
||||
|
||||
config := &oauth2.Config{
|
||||
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 nil, err
|
||||
}
|
||||
|
||||
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, nil)
|
||||
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) {
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package handlers
|
|||
import (
|
||||
"encoding/json"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/errors"
|
||||
"github.com/statping/statping/utils"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/github"
|
||||
|
@ -26,21 +27,20 @@ func githubOAuth(r *http.Request) (*oAuth, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
headers := []string{
|
||||
"Accept=application/vnd.github.machine-man-preview+json",
|
||||
"Authorization=token " + gg.AccessToken,
|
||||
}
|
||||
|
||||
resp, _, err := utils.HttpRequest("https://api.github.com/user", "GET", nil, headers, nil, 10*time.Second, true, nil)
|
||||
user, err := returnGithubUser(gg.AccessToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var user githubUser
|
||||
if err := json.Unmarshal(resp, &user); err != nil {
|
||||
orgs, err := returnGithubOrganizations(gg.AccessToken, user.Login)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if allowed := validateGithub(user, orgs); !allowed {
|
||||
return nil, errors.New("github user is not allowed to login")
|
||||
}
|
||||
|
||||
return &oAuth{
|
||||
Token: gg.AccessToken,
|
||||
RefreshToken: gg.RefreshToken,
|
||||
|
@ -51,6 +51,80 @@ func githubOAuth(r *http.Request) (*oAuth, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
func returnGithubUser(token string) (githubUser, error) {
|
||||
headers := []string{
|
||||
"Accept=application/vnd.github.machine-man-preview+json",
|
||||
"Authorization=token " + token,
|
||||
}
|
||||
resp, _, err := utils.HttpRequest("https://api.github.com/user", "GET", nil, headers, nil, 10*time.Second, true, nil)
|
||||
if err != nil {
|
||||
return githubUser{}, err
|
||||
}
|
||||
var user githubUser
|
||||
if err := json.Unmarshal(resp, &user); err != nil {
|
||||
return githubUser{}, err
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func returnGithubOrganizations(token, username string) ([]githubOrgs, error) {
|
||||
headers := []string{
|
||||
"Accept=application/vnd.github.machine-man-preview+json",
|
||||
"Authorization=token " + token,
|
||||
}
|
||||
resp, _, err := utils.HttpRequest("https://api.github.com/users/"+username+"/orgs", "GET", nil, headers, nil, 10*time.Second, true, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var orgs []githubOrgs
|
||||
if err := json.Unmarshal(resp, &orgs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return orgs, nil
|
||||
}
|
||||
|
||||
func validateGithub(ghUser githubUser, orgs []githubOrgs) bool {
|
||||
auth := core.App.OAuth
|
||||
if auth.GithubUsers == "" && auth.GithubOrgs == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
if auth.GithubUsers != "" {
|
||||
users := strings.Split(auth.GithubUsers, ",")
|
||||
for _, u := range users {
|
||||
if strings.ToLower(ghUser.Login) == strings.ToLower(u) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
if auth.GithubOrgs != "" {
|
||||
orgsAllowed := strings.Split(auth.GithubOrgs, ",")
|
||||
for _, o := range orgsAllowed {
|
||||
for _, org := range orgs {
|
||||
if strings.ToLower(o) == strings.ToLower(org.Login) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type githubOrgs struct {
|
||||
Login string `json:"login"`
|
||||
ID int `json:"id"`
|
||||
NodeID string `json:"node_id"`
|
||||
URL string `json:"url"`
|
||||
ReposURL string `json:"repos_url"`
|
||||
EventsURL string `json:"events_url"`
|
||||
HooksURL string `json:"hooks_url"`
|
||||
IssuesURL string `json:"issues_url"`
|
||||
MembersURL string `json:"members_url"`
|
||||
PublicMembersURL string `json:"public_members_url"`
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type githubUser struct {
|
||||
Login string `json:"login"`
|
||||
ID int `json:"id"`
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/statping/statping/types/core"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func googleOAuth(r *http.Request) (*oAuth, error) {
|
||||
c := core.App
|
||||
code := r.URL.Query().Get("code")
|
||||
|
||||
config := &oauth2.Config{
|
||||
ClientID: c.OAuth.GoogleClientID,
|
||||
ClientSecret: c.OAuth.GoogleClientSecret,
|
||||
Endpoint: google.Endpoint,
|
||||
}
|
||||
|
||||
gg, err := config.Exchange(r.Context(), code)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &oAuth{
|
||||
Token: gg.AccessToken,
|
||||
RefreshToken: gg.RefreshToken,
|
||||
Valid: gg.Valid(),
|
||||
}, nil
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/errors"
|
||||
"github.com/statping/statping/utils"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/slack"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func slackOAuth(r *http.Request) (*oAuth, error) {
|
||||
c := core.App
|
||||
code := r.URL.Query().Get("code")
|
||||
|
||||
config := &oauth2.Config{
|
||||
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 nil, err
|
||||
}
|
||||
|
||||
oauther := &oAuth{
|
||||
Token: gg.AccessToken,
|
||||
RefreshToken: gg.RefreshToken,
|
||||
Valid: gg.Valid(),
|
||||
Type: gg.Type(),
|
||||
}
|
||||
|
||||
identity, err := returnSlackIdentity(gg.AccessToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !identity.Ok {
|
||||
return nil, errors.New("slack identity is invalid")
|
||||
}
|
||||
|
||||
oauther.Username = identity.User.Name
|
||||
oauther.Email = identity.User.Email
|
||||
|
||||
return oauther, nil
|
||||
}
|
||||
|
||||
// slackIdentity will query the Slack API to fetch the users ID, username, and email address.
|
||||
func returnSlackIdentity(token string) (slackIdentity, error) {
|
||||
url := fmt.Sprintf("https://slack.com/api/users.identity?token=%s", token)
|
||||
out, _, err := utils.HttpRequest(url, "GET", "application/x-www-form-urlencoded", nil, nil, 10*time.Second, true, nil)
|
||||
if err != nil {
|
||||
return slackIdentity{}, err
|
||||
}
|
||||
|
||||
var i slackIdentity
|
||||
if err := json.Unmarshal(out, &i); err != nil {
|
||||
return slackIdentity{}, err
|
||||
}
|
||||
return i, 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"`
|
||||
}
|
|
@ -47,11 +47,14 @@ type OAuth struct {
|
|||
Providers string `gorm:"column:oauth_providers;" json:"oauth_providers"`
|
||||
GithubClientID string `gorm:"column:gh_client_id" json:"gh_client_id"`
|
||||
GithubClientSecret string `gorm:"column:gh_client_secret" json:"gh_client_secret" scope:"admin"`
|
||||
GithubUsers string `gorm:"column:gh_users" json:"gh_users" scope:"admin"`
|
||||
GithubOrgs string `gorm:"column:gh_orgs" json:"gh_orgs" scope:"admin"`
|
||||
GoogleClientID string `gorm:"column:google_client_id" json:"google_client_id"`
|
||||
GoogleClientSecret string `gorm:"column:google_client_secret" json:"google_client_secret" scope:"admin"`
|
||||
GoogleUsers string `gorm:"column:google_users" json:"google_users" scope:"admin"`
|
||||
SlackClientID string `gorm:"column:slack_client_id" json:"slack_client_id"`
|
||||
SlackClientSecret string `gorm:"column:slack_client_secret" json:"slack_client_secret" scope:"admin"`
|
||||
SlackTeam string `gorm:"column:slack_team" json:"slack_team"`
|
||||
SlackTeam string `gorm:"column:slack_team" json:"slack_team" scope:"admin"`
|
||||
}
|
||||
|
||||
// AllNotifiers contains all the Notifiers loaded
|
||||
|
|
Loading…
Reference in New Issue