frontend, api, db: add single sign-on
Closes https://gitlab.com/commento/commento/issues/90
This commit is contained in:
parent
536ec14b93
commit
1d1cd46c2b
@ -173,6 +173,7 @@ func commentListHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
"twitter": twitterConfigured && d.TwitterProvider,
|
"twitter": twitterConfigured && d.TwitterProvider,
|
||||||
"github": githubConfigured && d.GithubProvider,
|
"github": githubConfigured && d.GithubProvider,
|
||||||
"gitlab": gitlabConfigured && d.GitlabProvider,
|
"gitlab": gitlabConfigured && d.GitlabProvider,
|
||||||
|
"sso": d.SsoProvider,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -22,4 +22,7 @@ type domain struct {
|
|||||||
TwitterProvider bool `json:"twitterProvider"`
|
TwitterProvider bool `json:"twitterProvider"`
|
||||||
GithubProvider bool `json:"githubProvider"`
|
GithubProvider bool `json:"githubProvider"`
|
||||||
GitlabProvider bool `json:"gitlabProvider"`
|
GitlabProvider bool `json:"gitlabProvider"`
|
||||||
|
SsoProvider bool `json:"ssoProvider"`
|
||||||
|
SsoSecret string `json:"ssoSecret"`
|
||||||
|
SsoUrl string `json:"ssoUrl"`
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,10 @@ func domainGet(dmn string) (domain, error) {
|
|||||||
googleProvider,
|
googleProvider,
|
||||||
twitterProvider,
|
twitterProvider,
|
||||||
githubProvider,
|
githubProvider,
|
||||||
gitlabProvider
|
gitlabProvider,
|
||||||
|
ssoProvider,
|
||||||
|
ssoSecret,
|
||||||
|
ssoUrl
|
||||||
FROM domains
|
FROM domains
|
||||||
WHERE domain = $1;
|
WHERE domain = $1;
|
||||||
`
|
`
|
||||||
@ -48,7 +51,10 @@ func domainGet(dmn string) (domain, error) {
|
|||||||
&d.GoogleProvider,
|
&d.GoogleProvider,
|
||||||
&d.TwitterProvider,
|
&d.TwitterProvider,
|
||||||
&d.GithubProvider,
|
&d.GithubProvider,
|
||||||
&d.GitlabProvider); err != nil {
|
&d.GitlabProvider,
|
||||||
|
&d.SsoProvider,
|
||||||
|
&d.SsoSecret,
|
||||||
|
&d.SsoUrl); err != nil {
|
||||||
return d, errorNoSuchDomain
|
return d, errorNoSuchDomain
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,10 @@ func domainList(ownerHex string) ([]domain, error) {
|
|||||||
googleProvider,
|
googleProvider,
|
||||||
twitterProvider,
|
twitterProvider,
|
||||||
githubProvider,
|
githubProvider,
|
||||||
gitlabProvider
|
gitlabProvider,
|
||||||
|
ssoProvider,
|
||||||
|
ssoSecret,
|
||||||
|
ssoUrl
|
||||||
FROM domains
|
FROM domains
|
||||||
WHERE ownerHex=$1;
|
WHERE ownerHex=$1;
|
||||||
`
|
`
|
||||||
@ -56,7 +59,10 @@ func domainList(ownerHex string) ([]domain, error) {
|
|||||||
&d.GoogleProvider,
|
&d.GoogleProvider,
|
||||||
&d.TwitterProvider,
|
&d.TwitterProvider,
|
||||||
&d.GithubProvider,
|
&d.GithubProvider,
|
||||||
&d.GitlabProvider); err != nil {
|
&d.GitlabProvider,
|
||||||
|
&d.SsoProvider,
|
||||||
|
&d.SsoSecret,
|
||||||
|
&d.SsoUrl); err != nil {
|
||||||
logger.Errorf("cannot Scan domain: %v", err)
|
logger.Errorf("cannot Scan domain: %v", err)
|
||||||
return nil, errorInternal
|
return nil, errorInternal
|
||||||
}
|
}
|
||||||
|
69
api/domain_sso.go
Normal file
69
api/domain_sso.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func domainSsoNew(domain string) (string, error) {
|
||||||
|
if domain == "" {
|
||||||
|
return "", errorMissingField
|
||||||
|
}
|
||||||
|
|
||||||
|
ssoSecret, err := randomHex(32)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("error generating SSO secret hex: %v", err)
|
||||||
|
return "", errorInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
statement := `
|
||||||
|
UPDATE domains
|
||||||
|
SET ssoSecret = $2
|
||||||
|
WHERE domain = $1;
|
||||||
|
`
|
||||||
|
_, err = db.Exec(statement, domain, ssoSecret)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("cannot update ssoSecret: %v", err)
|
||||||
|
return "", errorInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
return ssoSecret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func domainSsoNewHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
type request struct {
|
||||||
|
OwnerToken *string `json:"ownerToken"`
|
||||||
|
Domain *string `json:"domain"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var x request
|
||||||
|
if err := bodyUnmarshal(r, &x); err != nil {
|
||||||
|
bodyMarshal(w, response{"success": false, "message": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
o, err := ownerGetByOwnerToken(*x.OwnerToken)
|
||||||
|
if err != nil {
|
||||||
|
bodyMarshal(w, response{"success": false, "message": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
domain := domainStrip(*x.Domain)
|
||||||
|
isOwner, err := domainOwnershipVerify(o.OwnerHex, domain)
|
||||||
|
if err != nil {
|
||||||
|
bodyMarshal(w, response{"success": false, "message": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isOwner {
|
||||||
|
bodyMarshal(w, response{"success": false, "message": errorNotAuthorised.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ssoSecret, err := domainSsoNew(domain)
|
||||||
|
if err != nil {
|
||||||
|
bodyMarshal(w, response{"success": false, "message": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyMarshal(w, response{"success": true, "ssoSecret": ssoSecret})
|
||||||
|
}
|
@ -5,6 +5,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func domainUpdate(d domain) error {
|
func domainUpdate(d domain) error {
|
||||||
|
if d.SsoProvider && d.SsoUrl == "" {
|
||||||
|
return errorMissingField
|
||||||
|
}
|
||||||
|
|
||||||
statement := `
|
statement := `
|
||||||
UPDATE domains
|
UPDATE domains
|
||||||
SET
|
SET
|
||||||
@ -19,7 +23,9 @@ func domainUpdate(d domain) error {
|
|||||||
googleProvider=$10,
|
googleProvider=$10,
|
||||||
twitterProvider=$11,
|
twitterProvider=$11,
|
||||||
githubProvider=$12,
|
githubProvider=$12,
|
||||||
gitlabProvider=$13
|
gitlabProvider=$13,
|
||||||
|
ssoProvider=$14,
|
||||||
|
ssoUrl=$15
|
||||||
WHERE domain=$1;
|
WHERE domain=$1;
|
||||||
`
|
`
|
||||||
|
|
||||||
@ -36,7 +42,9 @@ func domainUpdate(d domain) error {
|
|||||||
d.GoogleProvider,
|
d.GoogleProvider,
|
||||||
d.TwitterProvider,
|
d.TwitterProvider,
|
||||||
d.GithubProvider,
|
d.GithubProvider,
|
||||||
d.GitlabProvider)
|
d.GitlabProvider,
|
||||||
|
d.SsoProvider,
|
||||||
|
d.SsoUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("cannot update non-moderators: %v", err)
|
logger.Errorf("cannot update non-moderators: %v", err)
|
||||||
return errorInternal
|
return errorInternal
|
||||||
|
10
api/oauth_sso.go
Normal file
10
api/oauth_sso.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type ssoPayload struct {
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
Token string `json:"token"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Link string `json:"link"`
|
||||||
|
Photo string `json:"photo"`
|
||||||
|
}
|
110
api/oauth_sso_callback.go
Normal file
110
api/oauth_sso_callback.go
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ssoCallbackHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
payloadHex := r.FormValue("payload")
|
||||||
|
signature := r.FormValue("hmac")
|
||||||
|
|
||||||
|
payloadBytes, err := hex.DecodeString(payloadHex)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(w, "Error: invalid JSON payload hex encoding: %s\n", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
signatureBytes, err := hex.DecodeString(signature)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(w, "Error: invalid HMAC signature hex encoding: %s\n", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
payload := ssoPayload{}
|
||||||
|
err = json.Unmarshal(payloadBytes, &payload)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(w, "Error: cannot unmarshal JSON payload: %s\n", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if payload.Domain == "" || payload.Token == "" || payload.Email == "" || payload.Name == "" {
|
||||||
|
fmt.Fprintf(w, "Error: %s\n", errorMissingField.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if payload.Link == "" {
|
||||||
|
payload.Link = "undefined"
|
||||||
|
}
|
||||||
|
|
||||||
|
if payload.Photo == "" {
|
||||||
|
payload.Photo = "undefined"
|
||||||
|
}
|
||||||
|
|
||||||
|
d, err := domainGet(payload.Domain)
|
||||||
|
if err != nil {
|
||||||
|
if err == errorNoSuchDomain {
|
||||||
|
fmt.Fprintf(w, "Error: %s\n", err.Error())
|
||||||
|
} else {
|
||||||
|
logger.Errorf("cannot get domain for SSO: %v", err)
|
||||||
|
fmt.Fprintf(w, "Error: %s\n", errorInternal.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.SsoSecret == "" || d.SsoUrl == "" {
|
||||||
|
fmt.Fprintf(w, "Error: %s\n", errorMissingConfig.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := hex.DecodeString(d.SsoSecret)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("cannot decode SSO secret as hex: %v", err)
|
||||||
|
fmt.Fprintf(w, "Error: %s\n", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
h := hmac.New(sha256.New, key)
|
||||||
|
h.Write(payloadBytes)
|
||||||
|
expectedSignatureBytes := h.Sum(nil)
|
||||||
|
if !hmac.Equal(expectedSignatureBytes, signatureBytes) {
|
||||||
|
fmt.Fprintf(w, "Error: HMAC signature verification failed\n")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = commenterGetByCommenterToken(payload.Token)
|
||||||
|
if err != nil && err != errorNoSuchToken {
|
||||||
|
fmt.Fprintf(w, "Error: %s\n", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := commenterGetByEmail("sso:"+d.Domain, payload.Email)
|
||||||
|
if err != nil && err != errorNoSuchCommenter {
|
||||||
|
fmt.Fprintf(w, "Error: %s\n", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var commenterHex string
|
||||||
|
|
||||||
|
// TODO: in case of returning users, update the information we have on record?
|
||||||
|
if err == errorNoSuchCommenter {
|
||||||
|
commenterHex, err = commenterNew(payload.Email, payload.Name, payload.Link, payload.Photo, "sso:"+d.Domain, "")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(w, "Error: %s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
commenterHex = c.CommenterHex
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = commenterSessionUpdate(payload.Token, commenterHex); err != nil {
|
||||||
|
fmt.Fprintf(w, "Error: %s\n", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(w, "<html><script>window.parent.close()</script></html>")
|
||||||
|
}
|
82
api/oauth_sso_redirect.go
Normal file
82
api/oauth_sso_redirect.go
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ssoRedirectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
commenterToken := r.FormValue("commenterToken")
|
||||||
|
domain := r.Header.Get("Referer")
|
||||||
|
|
||||||
|
if commenterToken == "" {
|
||||||
|
fmt.Fprintf(w, "Error: %s\n", errorMissingField.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
domain = domainStrip(domain)
|
||||||
|
if domain == "" {
|
||||||
|
fmt.Fprintf(w, "Error: No Referer header found in request\n")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := commenterGetByCommenterToken(commenterToken)
|
||||||
|
if err != nil && err != errorNoSuchToken {
|
||||||
|
fmt.Fprintf(w, "Error: %s\n", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
d, err := domainGet(domain)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(w, "Error: %s\n", errorNoSuchDomain.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !d.SsoProvider {
|
||||||
|
fmt.Fprintf(w, "Error: SSO not configured for %s\n", domain)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.SsoSecret == "" || d.SsoUrl == "" {
|
||||||
|
fmt.Fprintf(w, "Error: %s\n", errorMissingConfig.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := hex.DecodeString(d.SsoSecret)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("cannot decode SSO secret as hex: %v", err)
|
||||||
|
fmt.Fprintf(w, "Error: %s\n", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenBytes, err := hex.DecodeString(commenterToken)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("cannot decode hex commenterToken: %v", err)
|
||||||
|
fmt.Fprintf(w, "Error: %s\n", errorInternal.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
h := hmac.New(sha256.New, key)
|
||||||
|
h.Write(tokenBytes)
|
||||||
|
signature := hex.EncodeToString(h.Sum(nil))
|
||||||
|
|
||||||
|
u, err := url.Parse(d.SsoUrl)
|
||||||
|
if err != nil {
|
||||||
|
// this should really not be happening; we're checking if the
|
||||||
|
// passed URL is valid at domain update
|
||||||
|
logger.Errorf("cannot parse URL: %v", err)
|
||||||
|
fmt.Fprintf(w, "Error: %s\n", errorInternal.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
q := u.Query()
|
||||||
|
q.Set("token", commenterToken)
|
||||||
|
q.Set("hmac", signature)
|
||||||
|
u.RawQuery = q.Encode()
|
||||||
|
|
||||||
|
http.Redirect(w, r, u.String(), http.StatusFound)
|
||||||
|
}
|
@ -15,6 +15,7 @@ func apiRouterInit(router *mux.Router) error {
|
|||||||
router.HandleFunc("/api/domain/new", domainNewHandler).Methods("POST")
|
router.HandleFunc("/api/domain/new", domainNewHandler).Methods("POST")
|
||||||
router.HandleFunc("/api/domain/delete", domainDeleteHandler).Methods("POST")
|
router.HandleFunc("/api/domain/delete", domainDeleteHandler).Methods("POST")
|
||||||
router.HandleFunc("/api/domain/clear", domainClearHandler).Methods("POST")
|
router.HandleFunc("/api/domain/clear", domainClearHandler).Methods("POST")
|
||||||
|
router.HandleFunc("/api/domain/sso/new", domainSsoNewHandler).Methods("POST")
|
||||||
router.HandleFunc("/api/domain/list", domainListHandler).Methods("POST")
|
router.HandleFunc("/api/domain/list", domainListHandler).Methods("POST")
|
||||||
router.HandleFunc("/api/domain/update", domainUpdateHandler).Methods("POST")
|
router.HandleFunc("/api/domain/update", domainUpdateHandler).Methods("POST")
|
||||||
router.HandleFunc("/api/domain/moderator/new", domainModeratorNewHandler).Methods("POST")
|
router.HandleFunc("/api/domain/moderator/new", domainModeratorNewHandler).Methods("POST")
|
||||||
@ -46,6 +47,9 @@ func apiRouterInit(router *mux.Router) error {
|
|||||||
router.HandleFunc("/api/oauth/gitlab/redirect", gitlabRedirectHandler).Methods("GET")
|
router.HandleFunc("/api/oauth/gitlab/redirect", gitlabRedirectHandler).Methods("GET")
|
||||||
router.HandleFunc("/api/oauth/gitlab/callback", gitlabCallbackHandler).Methods("GET")
|
router.HandleFunc("/api/oauth/gitlab/callback", gitlabCallbackHandler).Methods("GET")
|
||||||
|
|
||||||
|
router.HandleFunc("/api/oauth/sso/redirect", ssoRedirectHandler).Methods("GET")
|
||||||
|
router.HandleFunc("/api/oauth/sso/callback", ssoCallbackHandler).Methods("GET")
|
||||||
|
|
||||||
router.HandleFunc("/api/comment/new", commentNewHandler).Methods("POST")
|
router.HandleFunc("/api/comment/new", commentNewHandler).Methods("POST")
|
||||||
router.HandleFunc("/api/comment/list", commentListHandler).Methods("POST")
|
router.HandleFunc("/api/comment/list", commentListHandler).Methods("POST")
|
||||||
router.HandleFunc("/api/comment/count", commentCountHandler).Methods("POST")
|
router.HandleFunc("/api/comment/count", commentCountHandler).Methods("POST")
|
||||||
|
10
db/20190420181913-sso.sql
Normal file
10
db/20190420181913-sso.sql
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
-- Single Sign-On (SSO)
|
||||||
|
|
||||||
|
ALTER TABLE domains
|
||||||
|
ADD ssoProvider BOOLEAN NOT NULL DEFAULT false;
|
||||||
|
|
||||||
|
ALTER TABLE domains
|
||||||
|
ADD ssoSecret TEXT NOT NULL DEFAULT '';
|
||||||
|
|
||||||
|
ALTER TABLE domains
|
||||||
|
ADD ssoUrl TEXT NOT NULL DEFAULT '';
|
@ -292,6 +292,29 @@
|
|||||||
<label for="gitlab-provider">GitLab login</label>
|
<label for="gitlab-provider">GitLab login</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row no-border commento-round-check">
|
||||||
|
<input type="checkbox" v-model="domains[cd].ssoProvider" id="sso-provider" @change="window.commento.ssoProviderChangeHandler()">
|
||||||
|
<label for="sso-provider">Single sign-on</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="indent" v-if="domains[cd].ssoProvider">
|
||||||
|
<div class="row">
|
||||||
|
<div class="label">HMAC shared secret key</div>
|
||||||
|
<input class="input gray-input monospace" id="sso-secret" readonly="true" type="text" placeholder="Loading..." v-model="domains[cd].ssoSecret">
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="label">Redirect URL</div>
|
||||||
|
<input class="input gray-input" id="sso-url" type="text" :placeholder="domains[cd].ssoUrl" v-model="domains[cd].ssoUrl">
|
||||||
|
</div>
|
||||||
|
<div class="normal-text">
|
||||||
|
<div class="subtext-container">
|
||||||
|
<div class="subtext">
|
||||||
|
Read the Commento documentation <a href="https://docs.commento.io/configuration/frontend/sso.html">on single sign-on</a>.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="warning" v-if="!domains[cd].allowAnonymous && !domains[cd].commentoProvider && !domains[cd].googleProvider && !domains[cd].twitterProvider && !domains[cd].githubProvider && !domains[cd].gitlabProvider">
|
<div class="warning" v-if="!domains[cd].allowAnonymous && !domains[cd].commentoProvider && !domains[cd].googleProvider && !domains[cd].twitterProvider && !domains[cd].githubProvider && !domains[cd].gitlabProvider">
|
||||||
You have disabled all authentication options. Your readers will not be able to login, create comments, or vote.
|
You have disabled all authentication options. Your readers will not be able to login, create comments, or vote.
|
||||||
</div>
|
</div>
|
||||||
|
@ -1214,10 +1214,14 @@
|
|||||||
global.popupRender = function(id) {
|
global.popupRender = function(id) {
|
||||||
var loginBoxContainer = $(ID_LOGIN_BOX_CONTAINER);
|
var loginBoxContainer = $(ID_LOGIN_BOX_CONTAINER);
|
||||||
var loginBox = create("div");
|
var loginBox = create("div");
|
||||||
|
var ssoSubtitle = create("div");
|
||||||
|
var ssoButtonContainer = create("div");
|
||||||
|
var ssoButton = create("div");
|
||||||
|
var hr1 = create("hr");
|
||||||
var oauthSubtitle = create("div");
|
var oauthSubtitle = create("div");
|
||||||
var oauthButtonsContainer = create("div");
|
var oauthButtonsContainer = create("div");
|
||||||
var oauthButtons = create("div");
|
var oauthButtons = create("div");
|
||||||
var hr = create("hr");
|
var hr2 = create("hr");
|
||||||
var emailSubtitle = create("div");
|
var emailSubtitle = create("div");
|
||||||
var emailContainer = create("div");
|
var emailContainer = create("div");
|
||||||
var email = create("div");
|
var email = create("div");
|
||||||
@ -1233,7 +1237,7 @@
|
|||||||
emailButton.id = ID_LOGIN_BOX_EMAIL_BUTTON;
|
emailButton.id = ID_LOGIN_BOX_EMAIL_BUTTON;
|
||||||
loginLink.id = ID_LOGIN_BOX_LOGIN_LINK;
|
loginLink.id = ID_LOGIN_BOX_LOGIN_LINK;
|
||||||
loginLinkContainer.id = ID_LOGIN_BOX_LOGIN_LINK_CONTAINER;
|
loginLinkContainer.id = ID_LOGIN_BOX_LOGIN_LINK_CONTAINER;
|
||||||
hr.id = ID_LOGIN_BOX_HR;
|
hr2.id = ID_LOGIN_BOX_HR;
|
||||||
oauthSubtitle.id = ID_LOGIN_BOX_OAUTH_PRETEXT;
|
oauthSubtitle.id = ID_LOGIN_BOX_OAUTH_PRETEXT;
|
||||||
oauthButtonsContainer.id = ID_LOGIN_BOX_OAUTH_BUTTONS_CONTAINER;
|
oauthButtonsContainer.id = ID_LOGIN_BOX_OAUTH_BUTTONS_CONTAINER;
|
||||||
|
|
||||||
@ -1246,6 +1250,9 @@
|
|||||||
classAdd(emailButton, "email-button");
|
classAdd(emailButton, "email-button");
|
||||||
classAdd(loginLinkContainer, "login-link-container");
|
classAdd(loginLinkContainer, "login-link-container");
|
||||||
classAdd(loginLink, "login-link");
|
classAdd(loginLink, "login-link");
|
||||||
|
classAdd(ssoSubtitle, "login-box-subtitle");
|
||||||
|
classAdd(ssoButtonContainer, "oauth-buttons-container");
|
||||||
|
classAdd(ssoButton, "oauth-buttons");
|
||||||
classAdd(oauthSubtitle, "login-box-subtitle");
|
classAdd(oauthSubtitle, "login-box-subtitle");
|
||||||
classAdd(oauthButtonsContainer, "oauth-buttons-container");
|
classAdd(oauthButtonsContainer, "oauth-buttons-container");
|
||||||
classAdd(oauthButtons, "oauth-buttons");
|
classAdd(oauthButtons, "oauth-buttons");
|
||||||
@ -1256,6 +1263,7 @@
|
|||||||
emailSubtitle.innerText = "Login with your email address";
|
emailSubtitle.innerText = "Login with your email address";
|
||||||
emailButton.innerText = "Continue";
|
emailButton.innerText = "Continue";
|
||||||
oauthSubtitle.innerText = "Proceed with social login";
|
oauthSubtitle.innerText = "Proceed with social login";
|
||||||
|
ssoSubtitle.innerText = "Proceed with " + parent.location.host + " authentication";
|
||||||
|
|
||||||
onclick(emailButton, global.passwordAsk, id);
|
onclick(emailButton, global.passwordAsk, id);
|
||||||
onclick(loginLink, global.popupSwitch);
|
onclick(loginLink, global.popupSwitch);
|
||||||
@ -1274,7 +1282,7 @@
|
|||||||
var button = create("button");
|
var button = create("button");
|
||||||
|
|
||||||
classAdd(button, "button");
|
classAdd(button, "button");
|
||||||
classAdd(button, provider+ "-button");
|
classAdd(button, provider + "-button");
|
||||||
|
|
||||||
button.innerText = provider;
|
button.innerText = provider;
|
||||||
|
|
||||||
@ -1285,6 +1293,26 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (configuredOauths["sso"]) {
|
||||||
|
var button = create("button");
|
||||||
|
|
||||||
|
classAdd(button, "button");
|
||||||
|
classAdd(button, "sso-button");
|
||||||
|
|
||||||
|
button.innerText = "Login with Single Sign-On";
|
||||||
|
|
||||||
|
onclick(button, global.commentoAuth, {"provider": "sso", "id": id});
|
||||||
|
|
||||||
|
append(ssoButton, button);
|
||||||
|
append(ssoButtonContainer, ssoButton);
|
||||||
|
append(loginBox, ssoSubtitle);
|
||||||
|
append(loginBox, ssoButtonContainer);
|
||||||
|
|
||||||
|
if (numOauthConfigured > 0 || configuredOauths["commento"]) {
|
||||||
|
append(loginBox, hr1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (numOauthConfigured > 0) {
|
if (numOauthConfigured > 0) {
|
||||||
append(loginBox, oauthSubtitle);
|
append(loginBox, oauthSubtitle);
|
||||||
append(oauthButtonsContainer, oauthButtons);
|
append(oauthButtonsContainer, oauthButtons);
|
||||||
@ -1301,7 +1329,7 @@
|
|||||||
append(loginLinkContainer, loginLink);
|
append(loginLinkContainer, loginLink);
|
||||||
|
|
||||||
if (numOauthConfigured > 0 && configuredOauths["commento"]) {
|
if (numOauthConfigured > 0 && configuredOauths["commento"]) {
|
||||||
append(loginBox, hr);
|
append(loginBox, hr2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (configuredOauths["commento"]) {
|
if (configuredOauths["commento"]) {
|
||||||
|
@ -19,4 +19,27 @@
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
global.ssoProviderChangeHandler = function() {
|
||||||
|
var data = global.dashboard.$data;
|
||||||
|
|
||||||
|
if (data.domains[data.cd].ssoSecret === "") {
|
||||||
|
var json = {
|
||||||
|
"ownerToken": global.cookieGet("commentoOwnerToken"),
|
||||||
|
"domain": data.domains[data.cd].domain,
|
||||||
|
};
|
||||||
|
|
||||||
|
global.post(global.origin + "/api/domain/sso/new", json, function(resp) {
|
||||||
|
if (!resp.success) {
|
||||||
|
global.globalErrorShow(resp.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.domains[data.cd].ssoSecret = resp.ssoSecret;
|
||||||
|
$("#sso-secret").val(data.domains[data.cd].ssoSecret);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$("#sso-secret").val(data.domains[data.cd].ssoSecret);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} (window.commento, document));
|
} (window.commento, document));
|
||||||
|
@ -37,5 +37,12 @@
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
width: 70px;
|
width: 70px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.commento-sso-button {
|
||||||
|
background: #000000;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 13px;
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -367,6 +367,10 @@ body {
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 17px;
|
line-height: 17px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
|
a {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -540,6 +544,11 @@ body {
|
|||||||
.input::placeholder {
|
.input::placeholder {
|
||||||
color: $gray-4;
|
color: $gray-4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.monospace {
|
||||||
|
font-family: "Source Code Pro", monospace;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme {
|
.theme {
|
||||||
@ -598,7 +607,7 @@ body {
|
|||||||
|
|
||||||
.indent {
|
.indent {
|
||||||
margin-top: 0px;
|
margin-top: 0px;
|
||||||
padding-left: 32px;
|
padding-left: 35px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat {
|
.stat {
|
||||||
|
Loading…
Reference in New Issue
Block a user