Merge pull request #317 from pascoual/feature/generic-oauth

Login with generic oauth feature++
This commit is contained in:
Matt Baer 2020-08-17 14:24:50 -04:00 committed by GitHub
commit dfa14c9c92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 304 additions and 57 deletions

View File

@ -86,6 +86,11 @@ func apiSignup(app *App, w http.ResponseWriter, r *http.Request) error {
} }
func signup(app *App, w http.ResponseWriter, r *http.Request) (*AuthUser, error) { func signup(app *App, w http.ResponseWriter, r *http.Request) (*AuthUser, error) {
if app.cfg.App.DisablePasswordAuth {
err := ErrDisabledPasswordAuth
return nil, err
}
reqJSON := IsJSON(r) reqJSON := IsJSON(r)
// Get params // Get params
@ -299,16 +304,18 @@ func viewLogin(app *App, w http.ResponseWriter, r *http.Request) error {
p := &struct { p := &struct {
page.StaticPage page.StaticPage
To string To string
Message template.HTML Message template.HTML
Flashes []template.HTML Flashes []template.HTML
LoginUsername string LoginUsername string
OauthSlack bool OauthSlack bool
OauthWriteAs bool OauthWriteAs bool
OauthGitlab bool OauthGitlab bool
GitlabDisplayName string GitlabDisplayName string
OauthGitea bool OauthGeneric bool
GiteaDisplayName string OauthGenericDisplayName string
OauthGitea bool
GiteaDisplayName string
}{ }{
pageForReq(app, r), pageForReq(app, r),
r.FormValue("to"), r.FormValue("to"),
@ -318,6 +325,8 @@ func viewLogin(app *App, w http.ResponseWriter, r *http.Request) error {
app.Config().SlackOauth.ClientID != "", app.Config().SlackOauth.ClientID != "",
app.Config().WriteAsOauth.ClientID != "", app.Config().WriteAsOauth.ClientID != "",
app.Config().GitlabOauth.ClientID != "", app.Config().GitlabOauth.ClientID != "",
config.OrDefaultString(app.Config().GenericOauth.DisplayName, genericOauthDisplayName),
app.Config().GenericOauth.ClientID != "",
config.OrDefaultString(app.Config().GitlabOauth.DisplayName, gitlabDisplayName), config.OrDefaultString(app.Config().GitlabOauth.DisplayName, gitlabDisplayName),
app.Config().GiteaOauth.ClientID != "", app.Config().GiteaOauth.ClientID != "",
config.OrDefaultString(app.Config().GiteaOauth.DisplayName, giteaDisplayName), config.OrDefaultString(app.Config().GiteaOauth.DisplayName, giteaDisplayName),
@ -395,6 +404,11 @@ func login(app *App, w http.ResponseWriter, r *http.Request) error {
var err error var err error
var signin userCredentials var signin userCredentials
if app.cfg.App.DisablePasswordAuth {
err := ErrDisabledPasswordAuth
return err
}
// Log in with one-time token if one is given // Log in with one-time token if one is given
if oneTimeToken != "" { if oneTimeToken != "" {
log.Info("Login: Logging user in via token.") log.Info("Login: Logging user in via token.")
@ -1049,6 +1063,7 @@ func viewSettings(app *App, u *User, w http.ResponseWriter, r *http.Request) err
enableOauthSlack := app.Config().SlackOauth.ClientID != "" enableOauthSlack := app.Config().SlackOauth.ClientID != ""
enableOauthWriteAs := app.Config().WriteAsOauth.ClientID != "" enableOauthWriteAs := app.Config().WriteAsOauth.ClientID != ""
enableOauthGitLab := app.Config().GitlabOauth.ClientID != "" enableOauthGitLab := app.Config().GitlabOauth.ClientID != ""
enableOauthGeneric := app.Config().GenericOauth.ClientID != ""
enableOauthGitea := app.Config().GiteaOauth.ClientID != "" enableOauthGitea := app.Config().GiteaOauth.ClientID != ""
oauthAccounts, err := app.db.GetOauthAccounts(r.Context(), u.ID) oauthAccounts, err := app.db.GetOauthAccounts(r.Context(), u.ID)
@ -1056,7 +1071,7 @@ func viewSettings(app *App, u *User, w http.ResponseWriter, r *http.Request) err
log.Error("Unable to get oauth accounts for settings: %s", err) log.Error("Unable to get oauth accounts for settings: %s", err)
return impart.HTTPError{http.StatusInternalServerError, "Unable to retrieve user data. The humans have been alerted."} return impart.HTTPError{http.StatusInternalServerError, "Unable to retrieve user data. The humans have been alerted."}
} }
for _, oauthAccount := range oauthAccounts { for idx, oauthAccount := range oauthAccounts {
switch oauthAccount.Provider { switch oauthAccount.Provider {
case "slack": case "slack":
enableOauthSlack = false enableOauthSlack = false
@ -1064,41 +1079,49 @@ func viewSettings(app *App, u *User, w http.ResponseWriter, r *http.Request) err
enableOauthWriteAs = false enableOauthWriteAs = false
case "gitlab": case "gitlab":
enableOauthGitLab = false enableOauthGitLab = false
case "generic":
oauthAccounts[idx].DisplayName = app.Config().GenericOauth.DisplayName
oauthAccounts[idx].AllowDisconnect = app.Config().GenericOauth.AllowDisconnect
enableOauthGeneric = false
case "gitea": case "gitea":
enableOauthGitea = false enableOauthGitea = false
} }
} }
displayOauthSection := enableOauthSlack || enableOauthWriteAs || enableOauthGitLab || enableOauthGitea || len(oauthAccounts) > 0 displayOauthSection := enableOauthSlack || enableOauthWriteAs || enableOauthGitLab || enableOauthGeneric || enableOauthGitea || len(oauthAccounts) > 0
obj := struct { obj := struct {
*UserPage *UserPage
Email string Email string
HasPass bool HasPass bool
IsLogOut bool IsLogOut bool
Silenced bool Silenced bool
OauthSection bool OauthSection bool
OauthAccounts []oauthAccountInfo OauthAccounts []oauthAccountInfo
OauthSlack bool OauthSlack bool
OauthWriteAs bool OauthWriteAs bool
OauthGitLab bool OauthGitLab bool
GitLabDisplayName string GitLabDisplayName string
OauthGitea bool OauthGeneric bool
GiteaDisplayName string OauthGenericDisplayName string
OauthGitea bool
GiteaDisplayName string
}{ }{
UserPage: NewUserPage(app, r, u, "Account Settings", flashes), UserPage: NewUserPage(app, r, u, "Account Settings", flashes),
Email: fullUser.EmailClear(app.keys), Email: fullUser.EmailClear(app.keys),
HasPass: passIsSet, HasPass: passIsSet,
IsLogOut: r.FormValue("logout") == "1", IsLogOut: r.FormValue("logout") == "1",
Silenced: fullUser.IsSilenced(), Silenced: fullUser.IsSilenced(),
OauthSection: displayOauthSection, OauthSection: displayOauthSection,
OauthAccounts: oauthAccounts, OauthAccounts: oauthAccounts,
OauthSlack: enableOauthSlack, OauthSlack: enableOauthSlack,
OauthWriteAs: enableOauthWriteAs, OauthWriteAs: enableOauthWriteAs,
OauthGitLab: enableOauthGitLab, OauthGitLab: enableOauthGitLab,
GitLabDisplayName: config.OrDefaultString(app.Config().GitlabOauth.DisplayName, gitlabDisplayName), GitLabDisplayName: config.OrDefaultString(app.Config().GitlabOauth.DisplayName, gitlabDisplayName),
OauthGitea: enableOauthGitea, OauthGeneric: enableOauthGeneric,
GiteaDisplayName: config.OrDefaultString(app.Config().GiteaOauth.DisplayName, giteaDisplayName), OauthGenericDisplayName: config.OrDefaultString(app.Config().GenericOauth.DisplayName, genericOauthDisplayName),
OauthGitea: enableOauthGitea,
GiteaDisplayName: config.OrDefaultString(app.Config().GiteaOauth.DisplayName, giteaDisplayName),
} }
showUserPage(w, "settings", obj) showUserPage(w, "settings", obj)

17
app.go
View File

@ -243,9 +243,22 @@ func handleViewLanding(app *App, w http.ResponseWriter, r *http.Request) error {
Content template.HTML Content template.HTML
ForcedLanding bool ForcedLanding bool
OauthSlack bool
OauthWriteAs bool
OauthGitlab bool
OauthGeneric bool
OauthGenericDisplayName string
GitlabDisplayName string
}{ }{
StaticPage: pageForReq(app, r), StaticPage: pageForReq(app, r),
ForcedLanding: forceLanding, ForcedLanding: forceLanding,
OauthSlack: app.Config().SlackOauth.ClientID != "",
OauthWriteAs: app.Config().WriteAsOauth.ClientID != "",
OauthGitlab: app.Config().GitlabOauth.ClientID != "",
OauthGeneric: app.Config().GenericOauth.ClientID != "",
OauthGenericDisplayName: config.OrDefaultString(app.Config().GenericOauth.DisplayName, genericOauthDisplayName),
GitlabDisplayName: config.OrDefaultString(app.Config().GitlabOauth.DisplayName, gitlabDisplayName),
} }
banner, err := getLandingBanner(app) banner, err := getLandingBanner(app)

View File

@ -89,6 +89,18 @@ type (
CallbackProxyAPI string `ini:"callback_proxy_api"` CallbackProxyAPI string `ini:"callback_proxy_api"`
} }
GenericOauthCfg struct {
ClientID string `ini:"client_id"`
ClientSecret string `ini:"client_secret"`
Host string `ini:"host"`
DisplayName string `ini:"display_name"`
CallbackProxy string `ini:"callback_proxy"`
CallbackProxyAPI string `ini:"callback_proxy_api"`
TokenEndpoint string `ini:"token_endpoint"`
InspectEndpoint string `ini:"inspect_endpoint"`
AuthEndpoint string `ini:"auth_endpoint"`
AllowDisconnect bool `ini:"allow_disconnect"`
}
GiteaOauthCfg struct { GiteaOauthCfg struct {
ClientID string `ini:"client_id"` ClientID string `ini:"client_id"`
ClientSecret string `ini:"client_secret"` ClientSecret string `ini:"client_secret"`
@ -140,6 +152,9 @@ type (
// Check for Updates // Check for Updates
UpdateChecks bool `ini:"update_checks"` UpdateChecks bool `ini:"update_checks"`
// Disable password authentication if use only Oauth
DisablePasswordAuth bool `ini:"disable_password_auth"`
} }
// Config holds the complete configuration for running a writefreely instance // Config holds the complete configuration for running a writefreely instance
@ -150,6 +165,7 @@ type (
SlackOauth SlackOauthCfg `ini:"oauth.slack"` SlackOauth SlackOauthCfg `ini:"oauth.slack"`
WriteAsOauth WriteAsOauthCfg `ini:"oauth.writeas"` WriteAsOauth WriteAsOauthCfg `ini:"oauth.writeas"`
GitlabOauth GitlabOauthCfg `ini:"oauth.gitlab"` GitlabOauth GitlabOauthCfg `ini:"oauth.gitlab"`
GenericOauth GenericOauthCfg `ini:"oauth.generic"`
GiteaOauth GiteaOauthCfg `ini:"oauth.gitea"` GiteaOauth GiteaOauthCfg `ini:"oauth.gitea"`
} }
) )

View File

@ -2627,9 +2627,11 @@ func (db *datastore) GetIDForRemoteUser(ctx context.Context, remoteUserID, provi
} }
type oauthAccountInfo struct { type oauthAccountInfo struct {
Provider string Provider string
ClientID string ClientID string
RemoteUserID string RemoteUserID string
DisplayName string
AllowDisconnect bool
} }
func (db *datastore) GetOauthAccounts(ctx context.Context, userID int64) ([]oauthAccountInfo, error) { func (db *datastore) GetOauthAccounts(ctx context.Context, userID int64) ([]oauthAccountInfo, error) {

View File

@ -52,6 +52,8 @@ var (
ErrUserNotFoundEmail = impart.HTTPError{http.StatusNotFound, "Please enter your username instead of your email address."} ErrUserNotFoundEmail = impart.HTTPError{http.StatusNotFound, "Please enter your username instead of your email address."}
ErrUserSilenced = impart.HTTPError{http.StatusForbidden, "Account is silenced."} ErrUserSilenced = impart.HTTPError{http.StatusForbidden, "Account is silenced."}
ErrDisabledPasswordAuth = impart.HTTPError{http.StatusForbidden, "Password authentication is disabled."}
) )
// Post operation errors // Post operation errors

View File

@ -235,6 +235,33 @@ func configureGitlabOauth(parentHandler *Handler, r *mux.Router, app *App) {
} }
} }
func configureGenericOauth(parentHandler *Handler, r *mux.Router, app *App) {
if app.Config().GenericOauth.ClientID != "" {
callbackLocation := app.Config().App.Host + "/oauth/callback/generic"
var callbackProxy *callbackProxyClient = nil
if app.Config().GenericOauth.CallbackProxy != "" {
callbackProxy = &callbackProxyClient{
server: app.Config().GenericOauth.CallbackProxyAPI,
callbackLocation: app.Config().App.Host + "/oauth/callback/generic",
httpClient: config.DefaultHTTPClient(),
}
callbackLocation = app.Config().GenericOauth.CallbackProxy
}
oauthClient := genericOauthClient{
ClientID: app.Config().GenericOauth.ClientID,
ClientSecret: app.Config().GenericOauth.ClientSecret,
ExchangeLocation: app.Config().GenericOauth.Host + app.Config().GenericOauth.TokenEndpoint,
InspectLocation: app.Config().GenericOauth.Host + app.Config().GenericOauth.InspectEndpoint,
AuthLocation: app.Config().GenericOauth.Host + app.Config().GenericOauth.AuthEndpoint,
HttpClient: config.DefaultHTTPClient(),
CallbackLocation: callbackLocation,
}
configureOauthRoutes(parentHandler, r, app, oauthClient, callbackProxy)
}
}
func configureGiteaOauth(parentHandler *Handler, r *mux.Router, app *App) { func configureGiteaOauth(parentHandler *Handler, r *mux.Router, app *App) {
if app.Config().GiteaOauth.ClientID != "" { if app.Config().GiteaOauth.ClientID != "" {
callbackLocation := app.Config().App.Host + "/oauth/callback/gitea" callbackLocation := app.Config().App.Host + "/oauth/callback/gitea"

114
oauth_generic.go Normal file
View File

@ -0,0 +1,114 @@
package writefreely
import (
"context"
"errors"
"net/http"
"net/url"
"strings"
)
type genericOauthClient struct {
ClientID string
ClientSecret string
AuthLocation string
ExchangeLocation string
InspectLocation string
CallbackLocation string
HttpClient HttpClient
}
var _ oauthClient = genericOauthClient{}
const (
genericOauthDisplayName = "OAuth"
)
func (c genericOauthClient) GetProvider() string {
return "generic"
}
func (c genericOauthClient) GetClientID() string {
return c.ClientID
}
func (c genericOauthClient) GetCallbackLocation() string {
return c.CallbackLocation
}
func (c genericOauthClient) buildLoginURL(state string) (string, error) {
u, err := url.Parse(c.AuthLocation)
if err != nil {
return "", err
}
q := u.Query()
q.Set("client_id", c.ClientID)
q.Set("redirect_uri", c.CallbackLocation)
q.Set("response_type", "code")
q.Set("state", state)
q.Set("scope", "read_user")
u.RawQuery = q.Encode()
return u.String(), nil
}
func (c genericOauthClient) exchangeOauthCode(ctx context.Context, code string) (*TokenResponse, error) {
form := url.Values{}
form.Add("grant_type", "authorization_code")
form.Add("redirect_uri", c.CallbackLocation)
form.Add("scope", "read_user")
form.Add("code", code)
req, err := http.NewRequest("POST", c.ExchangeLocation, strings.NewReader(form.Encode()))
if err != nil {
return nil, err
}
req.WithContext(ctx)
req.Header.Set("User-Agent", "writefreely")
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.SetBasicAuth(c.ClientID, c.ClientSecret)
resp, err := c.HttpClient.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, errors.New("unable to exchange code for access token")
}
var tokenResponse TokenResponse
if err := limitedJsonUnmarshal(resp.Body, tokenRequestMaxLen, &tokenResponse); err != nil {
return nil, err
}
if tokenResponse.Error != "" {
return nil, errors.New(tokenResponse.Error)
}
return &tokenResponse, nil
}
func (c genericOauthClient) inspectOauthAccessToken(ctx context.Context, accessToken string) (*InspectResponse, error) {
req, err := http.NewRequest("GET", c.InspectLocation, nil)
if err != nil {
return nil, err
}
req.WithContext(ctx)
req.Header.Set("User-Agent", "writefreely")
req.Header.Set("Accept", "application/json")
req.Header.Set("Authorization", "Bearer "+accessToken)
resp, err := c.HttpClient.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, errors.New("unable to inspect access token")
}
var inspectResponse InspectResponse
if err := limitedJsonUnmarshal(resp.Body, infoRequestMaxLen, &inspectResponse); err != nil {
return nil, err
}
if inspectResponse.Error != "" {
return nil, errors.New(inspectResponse.Error)
}
return &inspectResponse, nil
}

View File

@ -60,6 +60,11 @@ form dd {
margin-top: 0; margin-top: 0;
max-width: 8em; max-width: 8em;
} }
#generic-oauth-login {
box-sizing: border-box;
font-size: 17px;
white-space:nowrap;
}
</style> </style>
{{end}} {{end}}
{{define "content"}} {{define "content"}}
@ -73,6 +78,21 @@ form dd {
<div{{if not .OpenRegistration}} style="padding: 2em 0;"{{end}}> <div{{if not .OpenRegistration}} style="padding: 2em 0;"{{end}}>
{{ if .OpenRegistration }} {{ if .OpenRegistration }}
{{ if or .OauthSlack .OauthWriteAs .OauthGitlab .OauthGeneric }}
{{ if .OauthSlack }}
<div class="row content-container signinbtns signinoauthbtns"><a class="loginbtn" href="/oauth/slack"><img alt="Sign in with Slack" height="40" width="172" src="/img/sign_in_with_slack.png" srcset="/img/sign_in_with_slack.png 1x, /img/sign_in_with_slack@2x.png 2x" /></a></div>
{{ end }}
{{ if .OauthWriteAs }}
<div class="row content-container signinbtns signinoauthbtns"><a class="btn cta loginbtn" id="writeas-login" href="/oauth/write.as">Sign in with <strong>Write.as</strong></a></div>
{{ end }}
{{ if .OauthGitlab }}
<div class="row content-container signinbtns signinoauthbtns"><a class="btn cta loginbtn" id="gitlab-login" href="/oauth/gitlab">Sign in with <strong>{{.GitlabDisplayName}}</strong></a></div>
{{ end }}
{{ if .OauthGeneric }}
<div class="row content-container signinbtns signinoauthbtns"><a class="btn cta loginbtn" id="generic-oauth-login" href="/oauth/generic">Sign in with <strong>{{ .OauthGenericDisplayName }}</strong></a></div>
{{ end }}
{{ end }}
{{if not .DisablePasswordAuth}}
{{if .Flashes}}<ul class="errors"> {{if .Flashes}}<ul class="errors">
{{range .Flashes}}<li class="urgent">{{.}}</li>{{end}} {{range .Flashes}}<li class="urgent">{{.}}</li>{{end}}
</ul>{{end}} </ul>{{end}}
@ -101,6 +121,7 @@ form dd {
</dl> </dl>
</form> </form>
</div> </div>
{{end}}
{{ else }} {{ else }}
<p style="font-size: 1.3em; margin: 1rem 0;">Registration is currently closed.</p> <p style="font-size: 1.3em; margin: 1rem 0;">Registration is currently closed.</p>
<p>You can always sign up on <a href="https://writefreely.org/instances">another instance</a>.</p> <p>You can always sign up on <a href="https://writefreely.org/instances">another instance</a>.</p>

View File

@ -3,6 +3,10 @@
<meta itemprop="description" content="Log in to {{.SiteName}}."> <meta itemprop="description" content="Log in to {{.SiteName}}.">
<style> <style>
input{margin-bottom:0.5em;} input{margin-bottom:0.5em;}
#generic-oauth-login {
box-sizing: border-box;
font-size: 17px;
}
</style> </style>
{{end}} {{end}}
{{define "content"}} {{define "content"}}
@ -13,7 +17,7 @@ input{margin-bottom:0.5em;}
{{range .Flashes}}<li class="urgent">{{.}}</li>{{end}} {{range .Flashes}}<li class="urgent">{{.}}</li>{{end}}
</ul>{{end}} </ul>{{end}}
{{ if or .OauthSlack .OauthWriteAs .OauthGitlab .OauthGitea }} {{ if or .OauthSlack .OauthWriteAs .OauthGitlab .OauthGeneric .OauthGitea }}
<div class="row content-container signinbtns"> <div class="row content-container signinbtns">
{{ if .OauthSlack }} {{ if .OauthSlack }}
<a class="loginbtn" href="/oauth/slack"><img alt="Sign in with Slack" height="40" width="172" src="/img/sign_in_with_slack.png" srcset="/img/sign_in_with_slack.png 1x, /img/sign_in_with_slack@2x.png 2x" /></a> <a class="loginbtn" href="/oauth/slack"><img alt="Sign in with Slack" height="40" width="172" src="/img/sign_in_with_slack.png" srcset="/img/sign_in_with_slack.png 1x, /img/sign_in_with_slack@2x.png 2x" /></a>
@ -24,17 +28,23 @@ input{margin-bottom:0.5em;}
{{ if .OauthGitlab }} {{ if .OauthGitlab }}
<a class="btn cta loginbtn" id="gitlab-login" href="/oauth/gitlab">Sign in with <strong>{{.GitlabDisplayName}}</strong></a> <a class="btn cta loginbtn" id="gitlab-login" href="/oauth/gitlab">Sign in with <strong>{{.GitlabDisplayName}}</strong></a>
{{ end }} {{ end }}
{{ if .OauthGeneric }}
<a class="btn cta loginbtn" id="generic-oauth-login" href="/oauth/generic">Sign in with <strong>{{ .OauthGenericDisplayName }}</strong></a>
{{ end }}
{{ if .OauthGitea }} {{ if .OauthGitea }}
<a class="btn cta loginbtn" id="gitea-login" href="/oauth/gitea">Sign in with <strong>{{.GiteaDisplayName}}</strong></a> <a class="btn cta loginbtn" id="gitea-login" href="/oauth/gitea">Sign in with <strong>{{.GiteaDisplayName}}</strong></a>
{{ end }} {{ end }}
</div> </div>
<div class="or"> {{if not .DisablePasswordAuth}}
<p>or</p> <div class="or">
<hr class="short" /> <p>or</p>
</div> <hr class="short" />
</div>
{{end}}
{{ end }} {{ end }}
{{if not .DisablePasswordAuth}}
<form action="/auth/login" method="post" style="text-align: center;margin-top:1em;" onsubmit="disableSubmit()"> <form action="/auth/login" method="post" style="text-align: center;margin-top:1em;" onsubmit="disableSubmit()">
<input type="text" name="alias" placeholder="Username" value="{{.LoginUsername}}" {{if not .LoginUsername}}autofocus{{end}} /><br /> <input type="text" name="alias" placeholder="Username" value="{{.LoginUsername}}" {{if not .LoginUsername}}autofocus{{end}} /><br />
<input type="password" name="pass" placeholder="Password" {{if .LoginUsername}}autofocus{{end}} /><br /> <input type="password" name="pass" placeholder="Password" {{if .LoginUsername}}autofocus{{end}} /><br />
@ -44,11 +54,12 @@ input{margin-bottom:0.5em;}
{{if and (not .SingleUser) .OpenRegistration}}<p style="text-align:center;font-size:0.9em;margin:3em auto;max-width:26em;">{{if .Message}}{{.Message}}{{else}}<em>No account yet?</em> <a href="{{.SignupPath}}">Sign up</a> to start a blog.{{end}}</p>{{end}} {{if and (not .SingleUser) .OpenRegistration}}<p style="text-align:center;font-size:0.9em;margin:3em auto;max-width:26em;">{{if .Message}}{{.Message}}{{else}}<em>No account yet?</em> <a href="{{.SignupPath}}">Sign up</a> to start a blog.{{end}}</p>{{end}}
<script type="text/javascript"> <script type="text/javascript">
function disableSubmit() { function disableSubmit() {
var $btn = document.getElementById("btn-login"); var $btn = document.getElementById("btn-login");
$btn.value = "Logging in..."; $btn.value = "Logging in...";
$btn.disabled = true; $btn.disabled = true;
} }
</script> </script>
{{end}}
{{end}} {{end}}

View File

@ -76,6 +76,7 @@ func InitRoutes(apper Apper, r *mux.Router) *mux.Router {
configureSlackOauth(handler, write, apper.App()) configureSlackOauth(handler, write, apper.App())
configureWriteAsOauth(handler, write, apper.App()) configureWriteAsOauth(handler, write, apper.App())
configureGitlabOauth(handler, write, apper.App()) configureGitlabOauth(handler, write, apper.App())
configureGenericOauth(handler, write, apper.App())
configureGiteaOauth(handler, write, apper.App()) configureGiteaOauth(handler, write, apper.App())
// Set up dyamic page handlers // Set up dyamic page handlers

View File

@ -41,6 +41,7 @@ h3 { font-weight: normal; }
</form> </form>
{{ end }} {{ end }}
{{if not .DisablePasswordAuth}}
<form method="post" action="/api/me/self" autocomplete="false"> <form method="post" action="/api/me/self" autocomplete="false">
<input type="hidden" name="logout" value="{{.IsLogOut}}" /> <input type="hidden" name="logout" value="{{.IsLogOut}}" />
<div class="option"> <div class="option">
@ -72,6 +73,7 @@ h3 { font-weight: normal; }
<input type="submit" value="Save changes" tabindex="4" /> <input type="submit" value="Save changes" tabindex="4" />
</div> </div>
</form> </form>
{{end}}
{{ if .OauthSection }} {{ if .OauthSection }}
<hr /> <hr />
@ -86,14 +88,22 @@ h3 { font-weight: normal; }
<input type="hidden" name="client_id" value="{{ $oauth_account.ClientID }}" /> <input type="hidden" name="client_id" value="{{ $oauth_account.ClientID }}" />
<input type="hidden" name="remote_user_id" value="{{ $oauth_account.RemoteUserID }}" /> <input type="hidden" name="remote_user_id" value="{{ $oauth_account.RemoteUserID }}" />
<div class="section oauth-provider"> <div class="section oauth-provider">
<img src="/img/mark/{{$oauth_account.Provider}}.png" alt="{{ $oauth_account.Provider | title }}" /> {{ if $oauth_account.DisplayName}}
<input type="submit" value="Remove {{ $oauth_account.Provider | title }}" /> {{ if $oauth_account.AllowDisconnect}}
<input type="submit" value="Remove {{.DisplayName}}" />
{{else}}
<a class="btn cta"><strong>{{.DisplayName}}</strong></a>
{{end}}
{{else}}
<img src="/img/mark/{{$oauth_account.Provider}}.png" alt="{{ $oauth_account.Provider | title }}" />
<input type="submit" value="Remove {{ $oauth_account.Provider | title }}" />
{{end}}
</div> </div>
</form> </form>
{{ end }} {{ end }}
</div> </div>
{{ end }} {{ end }}
{{ if or .OauthSlack .OauthWriteAs .OauthGitLab .OauthGitea }} {{ if or .OauthSlack .OauthWriteAs .OauthGitLab .OauthGeneric .OauthGitea }}
<div class="option"> <div class="option">
<h2>Link External Accounts</h2> <h2>Link External Accounts</h2>
<p>Connect additional accounts to enable logging in with those providers, instead of using your username and password.</p> <p>Connect additional accounts to enable logging in with those providers, instead of using your username and password.</p>
@ -131,6 +141,13 @@ h3 { font-weight: normal; }
</div> </div>
{{ end }} {{ end }}
</div> </div>
{{ if .OauthGeneric }}
<div class="row">
<div class="section oauth-provider">
<p><a class="btn cta loginbtn" id="generic-oauth-login" href="/oauth/generic?attach=t">Link <strong>{{ .OauthGenericDisplayName }}</strong></a></p>
</div>
</div>
{{ end }}
</div> </div>
{{ end }} {{ end }}
{{ end }} {{ end }}