code.gitea.io/gitea@v1.19.3/modules/context/auth.go (about)

     1  // Copyright 2014 The Gogs Authors. All rights reserved.
     2  // Copyright 2019 The Gitea Authors. All rights reserved.
     3  // SPDX-License-Identifier: MIT
     4  
     5  package context
     6  
     7  import (
     8  	"net/http"
     9  	"strings"
    10  
    11  	"code.gitea.io/gitea/models/auth"
    12  	"code.gitea.io/gitea/modules/log"
    13  	"code.gitea.io/gitea/modules/setting"
    14  	"code.gitea.io/gitea/modules/web/middleware"
    15  )
    16  
    17  // ToggleOptions contains required or check options
    18  type ToggleOptions struct {
    19  	SignInRequired  bool
    20  	SignOutRequired bool
    21  	AdminRequired   bool
    22  	DisableCSRF     bool
    23  }
    24  
    25  // Toggle returns toggle options as middleware
    26  func Toggle(options *ToggleOptions) func(ctx *Context) {
    27  	return func(ctx *Context) {
    28  		// Check prohibit login users.
    29  		if ctx.IsSigned {
    30  			if !ctx.Doer.IsActive && setting.Service.RegisterEmailConfirm {
    31  				ctx.Data["Title"] = ctx.Tr("auth.active_your_account")
    32  				ctx.HTML(http.StatusOK, "user/auth/activate")
    33  				return
    34  			}
    35  			if !ctx.Doer.IsActive || ctx.Doer.ProhibitLogin {
    36  				log.Info("Failed authentication attempt for %s from %s", ctx.Doer.Name, ctx.RemoteAddr())
    37  				ctx.Data["Title"] = ctx.Tr("auth.prohibit_login")
    38  				ctx.HTML(http.StatusOK, "user/auth/prohibit_login")
    39  				return
    40  			}
    41  
    42  			if ctx.Doer.MustChangePassword {
    43  				if ctx.Req.URL.Path != "/user/settings/change_password" {
    44  					if strings.HasPrefix(ctx.Req.UserAgent(), "git") {
    45  						ctx.Error(http.StatusUnauthorized, ctx.Tr("auth.must_change_password"))
    46  						return
    47  					}
    48  					ctx.Data["Title"] = ctx.Tr("auth.must_change_password")
    49  					ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/change_password"
    50  					if ctx.Req.URL.Path != "/user/events" {
    51  						middleware.SetRedirectToCookie(ctx.Resp, setting.AppSubURL+ctx.Req.URL.RequestURI())
    52  					}
    53  					ctx.Redirect(setting.AppSubURL + "/user/settings/change_password")
    54  					return
    55  				}
    56  			} else if ctx.Req.URL.Path == "/user/settings/change_password" {
    57  				// make sure that the form cannot be accessed by users who don't need this
    58  				ctx.Redirect(setting.AppSubURL + "/")
    59  				return
    60  			}
    61  		}
    62  
    63  		// Redirect to dashboard if user tries to visit any non-login page.
    64  		if options.SignOutRequired && ctx.IsSigned && ctx.Req.URL.RequestURI() != "/" {
    65  			ctx.Redirect(setting.AppSubURL + "/")
    66  			return
    67  		}
    68  
    69  		if !options.SignOutRequired && !options.DisableCSRF && ctx.Req.Method == "POST" {
    70  			ctx.csrf.Validate(ctx)
    71  			if ctx.Written() {
    72  				return
    73  			}
    74  		}
    75  
    76  		if options.SignInRequired {
    77  			if !ctx.IsSigned {
    78  				if ctx.Req.URL.Path != "/user/events" {
    79  					middleware.SetRedirectToCookie(ctx.Resp, setting.AppSubURL+ctx.Req.URL.RequestURI())
    80  				}
    81  				ctx.Redirect(setting.AppSubURL + "/user/login")
    82  				return
    83  			} else if !ctx.Doer.IsActive && setting.Service.RegisterEmailConfirm {
    84  				ctx.Data["Title"] = ctx.Tr("auth.active_your_account")
    85  				ctx.HTML(http.StatusOK, "user/auth/activate")
    86  				return
    87  			}
    88  		}
    89  
    90  		// Redirect to log in page if auto-signin info is provided and has not signed in.
    91  		if !options.SignOutRequired && !ctx.IsSigned &&
    92  			len(ctx.GetCookie(setting.CookieUserName)) > 0 {
    93  			if ctx.Req.URL.Path != "/user/events" {
    94  				middleware.SetRedirectToCookie(ctx.Resp, setting.AppSubURL+ctx.Req.URL.RequestURI())
    95  			}
    96  			ctx.Redirect(setting.AppSubURL + "/user/login")
    97  			return
    98  		}
    99  
   100  		if options.AdminRequired {
   101  			if !ctx.Doer.IsAdmin {
   102  				ctx.Error(http.StatusForbidden)
   103  				return
   104  			}
   105  			ctx.Data["PageIsAdmin"] = true
   106  		}
   107  	}
   108  }
   109  
   110  // ToggleAPI returns toggle options as middleware
   111  func ToggleAPI(options *ToggleOptions) func(ctx *APIContext) {
   112  	return func(ctx *APIContext) {
   113  		// Check prohibit login users.
   114  		if ctx.IsSigned {
   115  			if !ctx.Doer.IsActive && setting.Service.RegisterEmailConfirm {
   116  				ctx.Data["Title"] = ctx.Tr("auth.active_your_account")
   117  				ctx.JSON(http.StatusForbidden, map[string]string{
   118  					"message": "This account is not activated.",
   119  				})
   120  				return
   121  			}
   122  			if !ctx.Doer.IsActive || ctx.Doer.ProhibitLogin {
   123  				log.Info("Failed authentication attempt for %s from %s", ctx.Doer.Name, ctx.RemoteAddr())
   124  				ctx.Data["Title"] = ctx.Tr("auth.prohibit_login")
   125  				ctx.JSON(http.StatusForbidden, map[string]string{
   126  					"message": "This account is prohibited from signing in, please contact your site administrator.",
   127  				})
   128  				return
   129  			}
   130  
   131  			if ctx.Doer.MustChangePassword {
   132  				ctx.JSON(http.StatusForbidden, map[string]string{
   133  					"message": "You must change your password. Change it at: " + setting.AppURL + "/user/change_password",
   134  				})
   135  				return
   136  			}
   137  		}
   138  
   139  		// Redirect to dashboard if user tries to visit any non-login page.
   140  		if options.SignOutRequired && ctx.IsSigned && ctx.Req.URL.RequestURI() != "/" {
   141  			ctx.Redirect(setting.AppSubURL + "/")
   142  			return
   143  		}
   144  
   145  		if options.SignInRequired {
   146  			if !ctx.IsSigned {
   147  				// Restrict API calls with error message.
   148  				ctx.JSON(http.StatusForbidden, map[string]string{
   149  					"message": "Only signed in user is allowed to call APIs.",
   150  				})
   151  				return
   152  			} else if !ctx.Doer.IsActive && setting.Service.RegisterEmailConfirm {
   153  				ctx.Data["Title"] = ctx.Tr("auth.active_your_account")
   154  				ctx.HTML(http.StatusOK, "user/auth/activate")
   155  				return
   156  			}
   157  			if ctx.IsSigned && ctx.IsBasicAuth {
   158  				if skip, ok := ctx.Data["SkipLocalTwoFA"]; ok && skip.(bool) {
   159  					return // Skip 2FA
   160  				}
   161  				twofa, err := auth.GetTwoFactorByUID(ctx.Doer.ID)
   162  				if err != nil {
   163  					if auth.IsErrTwoFactorNotEnrolled(err) {
   164  						return // No 2FA enrollment for this user
   165  					}
   166  					ctx.InternalServerError(err)
   167  					return
   168  				}
   169  				otpHeader := ctx.Req.Header.Get("X-Gitea-OTP")
   170  				ok, err := twofa.ValidateTOTP(otpHeader)
   171  				if err != nil {
   172  					ctx.InternalServerError(err)
   173  					return
   174  				}
   175  				if !ok {
   176  					ctx.JSON(http.StatusForbidden, map[string]string{
   177  						"message": "Only signed in user is allowed to call APIs.",
   178  					})
   179  					return
   180  				}
   181  			}
   182  		}
   183  
   184  		if options.AdminRequired {
   185  			if !ctx.Doer.IsAdmin {
   186  				ctx.JSON(http.StatusForbidden, map[string]string{
   187  					"message": "You have no permission to request for this.",
   188  				})
   189  				return
   190  			}
   191  			ctx.Data["PageIsAdmin"] = true
   192  		}
   193  	}
   194  }