2014-12-29 13:36:08 +01:00
|
|
|
package api
|
|
|
|
|
|
|
|
|
|
import (
|
2019-01-23 17:01:09 +01:00
|
|
|
"encoding/hex"
|
|
|
|
|
"net/http"
|
2015-01-27 12:05:23 +01:00
|
|
|
"net/url"
|
|
|
|
|
|
2015-02-05 10:37:13 +01:00
|
|
|
"github.com/grafana/grafana/pkg/api/dtos"
|
|
|
|
|
"github.com/grafana/grafana/pkg/bus"
|
|
|
|
|
"github.com/grafana/grafana/pkg/log"
|
2015-07-15 10:08:23 +02:00
|
|
|
"github.com/grafana/grafana/pkg/login"
|
2015-03-22 15:14:00 -04:00
|
|
|
"github.com/grafana/grafana/pkg/metrics"
|
2015-02-05 10:37:13 +01:00
|
|
|
m "github.com/grafana/grafana/pkg/models"
|
|
|
|
|
"github.com/grafana/grafana/pkg/setting"
|
2019-01-23 17:01:09 +01:00
|
|
|
"github.com/grafana/grafana/pkg/util"
|
2014-12-29 13:36:08 +01:00
|
|
|
)
|
|
|
|
|
|
2015-01-27 12:05:23 +01:00
|
|
|
const (
|
2019-01-23 17:01:09 +01:00
|
|
|
ViewIndex = "index"
|
|
|
|
|
LoginErrorCookieName = "login_error"
|
2015-01-27 12:05:23 +01:00
|
|
|
)
|
|
|
|
|
|
2018-10-12 11:26:42 +02:00
|
|
|
func (hs *HTTPServer) LoginView(c *m.ReqContext) {
|
|
|
|
|
viewData, err := hs.setIndexViewData(c)
|
2015-11-20 09:43:10 +01:00
|
|
|
if err != nil {
|
2015-01-27 10:09:54 +01:00
|
|
|
c.Handle(500, "Failed to get settings", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-28 15:10:50 +02:00
|
|
|
enabledOAuths := make(map[string]interface{})
|
|
|
|
|
for key, oauth := range setting.OAuthService.OAuthInfos {
|
|
|
|
|
enabledOAuths[key] = map[string]string{"name": oauth.Name}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
viewData.Settings["oauth"] = enabledOAuths
|
2015-11-20 09:43:10 +01:00
|
|
|
viewData.Settings["disableUserSignUp"] = !setting.AllowUserSignUp
|
2015-12-14 17:28:57 +01:00
|
|
|
viewData.Settings["loginHint"] = setting.LoginHint
|
2016-09-28 15:27:08 +02:00
|
|
|
viewData.Settings["disableLoginForm"] = setting.DisableLoginForm
|
2015-01-28 10:26:13 +01:00
|
|
|
|
2019-01-23 17:01:09 +01:00
|
|
|
if loginError, ok := tryGetEncryptedCookie(c, LoginErrorCookieName); ok {
|
|
|
|
|
deleteCookie(c, LoginErrorCookieName)
|
2017-02-01 16:32:51 +03:00
|
|
|
viewData.Settings["loginError"] = loginError
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-28 16:16:48 +02:00
|
|
|
if tryOAuthAutoLogin(c) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-23 15:28:33 +01:00
|
|
|
if !c.IsSignedIn {
|
|
|
|
|
c.HTML(200, ViewIndex, viewData)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if redirectTo, _ := url.QueryUnescape(c.GetCookie("redirect_to")); len(redirectTo) > 0 {
|
|
|
|
|
c.SetCookie("redirect_to", "", -1, setting.AppSubUrl+"/")
|
|
|
|
|
c.Redirect(redirectTo)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c.Redirect(setting.AppSubUrl + "/")
|
2015-04-07 09:25:00 +02:00
|
|
|
}
|
|
|
|
|
|
2018-05-28 16:16:48 +02:00
|
|
|
func tryOAuthAutoLogin(c *m.ReqContext) bool {
|
|
|
|
|
if !setting.OAuthAutoLogin {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
oauthInfos := setting.OAuthService.OAuthInfos
|
|
|
|
|
if len(oauthInfos) != 1 {
|
|
|
|
|
log.Warn("Skipping OAuth auto login because multiple OAuth providers are configured.")
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
for key := range setting.OAuthService.OAuthInfos {
|
|
|
|
|
redirectUrl := setting.AppSubUrl + "/login/" + key
|
|
|
|
|
log.Info("OAuth auto login enabled. Redirecting to " + redirectUrl)
|
|
|
|
|
c.Redirect(redirectUrl, 307)
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-04 17:37:07 +01:00
|
|
|
func (hs *HTTPServer) LoginAPIPing(c *m.ReqContext) Response {
|
|
|
|
|
if c.IsSignedIn || c.IsAnonymous {
|
|
|
|
|
return JSON(200, "Logged in")
|
2015-01-27 12:05:23 +01:00
|
|
|
}
|
|
|
|
|
|
2019-02-04 17:37:07 +01:00
|
|
|
return Error(401, "Unauthorized", nil)
|
2015-01-27 10:09:54 +01:00
|
|
|
}
|
|
|
|
|
|
2019-01-15 15:15:52 +01:00
|
|
|
func (hs *HTTPServer) LoginPost(c *m.ReqContext, cmd dtos.LoginCommand) Response {
|
2017-03-17 16:35:05 -04:00
|
|
|
if setting.DisableLoginForm {
|
2018-03-22 22:13:46 +01:00
|
|
|
return Error(401, "Login is disabled", nil)
|
2017-03-17 16:35:05 -04:00
|
|
|
}
|
|
|
|
|
|
2018-03-23 15:50:07 -04:00
|
|
|
authQuery := &m.LoginUserQuery{
|
|
|
|
|
ReqContext: c,
|
|
|
|
|
Username: cmd.User,
|
|
|
|
|
Password: cmd.Password,
|
|
|
|
|
IpAddress: c.Req.RemoteAddr,
|
2014-12-29 13:36:08 +01:00
|
|
|
}
|
|
|
|
|
|
2018-03-23 15:50:07 -04:00
|
|
|
if err := bus.Dispatch(authQuery); err != nil {
|
2018-01-26 10:41:41 +01:00
|
|
|
if err == login.ErrInvalidCredentials || err == login.ErrTooManyLoginAttempts {
|
2018-03-22 22:13:46 +01:00
|
|
|
return Error(401, "Invalid username or password", err)
|
2015-06-04 09:34:42 +02:00
|
|
|
}
|
|
|
|
|
|
2018-03-22 22:13:46 +01:00
|
|
|
return Error(500, "Error while trying to authenticate user", err)
|
2014-12-29 13:36:08 +01:00
|
|
|
}
|
|
|
|
|
|
2015-07-10 11:10:48 +02:00
|
|
|
user := authQuery.User
|
|
|
|
|
|
2019-01-15 15:15:52 +01:00
|
|
|
hs.loginUserWithUser(user, c)
|
2014-12-29 13:36:08 +01:00
|
|
|
|
2015-01-27 12:05:23 +01:00
|
|
|
result := map[string]interface{}{
|
|
|
|
|
"message": "Logged in",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if redirectTo, _ := url.QueryUnescape(c.GetCookie("redirect_to")); len(redirectTo) > 0 {
|
|
|
|
|
result["redirectUrl"] = redirectTo
|
|
|
|
|
c.SetCookie("redirect_to", "", -1, setting.AppSubUrl+"/")
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-06 11:23:52 +02:00
|
|
|
metrics.M_Api_Login_Post.Inc()
|
2015-03-22 15:14:00 -04:00
|
|
|
|
2018-03-22 22:13:46 +01:00
|
|
|
return JSON(200, result)
|
2015-06-04 09:34:42 +02:00
|
|
|
}
|
|
|
|
|
|
2019-01-15 15:15:52 +01:00
|
|
|
func (hs *HTTPServer) loginUserWithUser(user *m.User, c *m.ReqContext) {
|
2015-01-19 18:01:04 +01:00
|
|
|
if user == nil {
|
2019-01-15 15:15:52 +01:00
|
|
|
hs.log.Error("User login with nil user")
|
2014-12-29 13:36:08 +01:00
|
|
|
}
|
|
|
|
|
|
2019-01-15 15:15:52 +01:00
|
|
|
err := hs.AuthTokenService.UserAuthenticatedHook(user, c)
|
|
|
|
|
if err != nil {
|
2019-01-17 17:32:33 +01:00
|
|
|
hs.log.Error("User auth hook failed", "error", err)
|
2016-03-07 17:26:31 +01:00
|
|
|
}
|
2014-12-29 13:36:08 +01:00
|
|
|
}
|
|
|
|
|
|
2019-01-15 15:15:52 +01:00
|
|
|
func (hs *HTTPServer) Logout(c *m.ReqContext) {
|
|
|
|
|
hs.AuthTokenService.UserSignedOutHook(c)
|
|
|
|
|
|
2018-05-27 14:52:50 +02:00
|
|
|
if setting.SignoutRedirectUrl != "" {
|
|
|
|
|
c.Redirect(setting.SignoutRedirectUrl)
|
|
|
|
|
} else {
|
|
|
|
|
c.Redirect(setting.AppSubUrl + "/login")
|
|
|
|
|
}
|
2014-12-29 13:36:08 +01:00
|
|
|
}
|
2019-01-23 17:01:09 +01:00
|
|
|
|
|
|
|
|
func tryGetEncryptedCookie(ctx *m.ReqContext, cookieName string) (string, bool) {
|
|
|
|
|
cookie := ctx.GetCookie(cookieName)
|
|
|
|
|
if cookie == "" {
|
|
|
|
|
return "", false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
decoded, err := hex.DecodeString(cookie)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
decryptedError, err := util.Decrypt([]byte(decoded), setting.SecretKey)
|
|
|
|
|
return string(decryptedError), err == nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func deleteCookie(ctx *m.ReqContext, cookieName string) {
|
|
|
|
|
ctx.SetCookie(cookieName, "", -1, setting.AppSubUrl+"/")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (hs *HTTPServer) trySetEncryptedCookie(ctx *m.ReqContext, cookieName string, value string, maxAge int) error {
|
|
|
|
|
encryptedError, err := util.Encrypt([]byte(value), setting.SecretKey)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
http.SetCookie(ctx.Resp, &http.Cookie{
|
|
|
|
|
Name: cookieName,
|
|
|
|
|
MaxAge: 60,
|
|
|
|
|
Value: hex.EncodeToString(encryptedError),
|
|
|
|
|
HttpOnly: true,
|
|
|
|
|
Path: setting.AppSubUrl + "/",
|
2019-01-24 19:04:58 +01:00
|
|
|
Secure: hs.Cfg.SecurityHTTPSCookies,
|
2019-01-23 17:01:09 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|