Skip to content

Commit

Permalink
Prevent ReverseProxy users from changing username
Browse files Browse the repository at this point in the history
Fix go-gitea#2407

Signed-off-by: Andrew Thornton <[email protected]>
  • Loading branch information
zeripath committed Apr 5, 2021
1 parent bead568 commit 41c8ccf
Show file tree
Hide file tree
Showing 11 changed files with 34 additions and 21 deletions.
2 changes: 1 addition & 1 deletion integrations/api_repo_lfs_locks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func TestAPILFSLocksNotLogin(t *testing.T) {
resp := MakeRequest(t, req, http.StatusUnauthorized)
var lfsLockError api.LFSLockError
DecodeJSON(t, resp, &lfsLockError)
assert.Equal(t, "Unauthorized", lfsLockError.Message)
assert.Equal(t, "You must have pull access to list locks", lfsLockError.Message)
}

func TestAPILFSLocksLogged(t *testing.T) {
Expand Down
3 changes: 3 additions & 0 deletions modules/auth/sso/basic.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ import (
"code.gitea.io/gitea/modules/timeutil"
)

// BasicAuthenticationMechanism represents authentication using Basic authentication
const BasicAuthenticationMechanism AuthenticationMechanism = "Basic"

// Ensure the struct implements the interface.
var (
_ SingleSignOn = &Basic{}
Expand Down
6 changes: 6 additions & 0 deletions modules/auth/sso/oauth2.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ import (
"code.gitea.io/gitea/modules/web/middleware"
)

// OAuth2Mechanism represents authentication using OAuth2
const OAuth2Mechanism AuthenticationMechanism = "OAuth2"

// TokenMechanism represents authentication using Token
const TokenMechanism AuthenticationMechanism = "Token"

// Ensure the struct implements the interface.
var (
_ SingleSignOn = &OAuth2{}
Expand Down
4 changes: 3 additions & 1 deletion modules/auth/sso/reverseproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ import (
gouuid "github.com/google/uuid"
)

// ReverseProxyMechanism represents authentication using ReverseProxy
const ReverseProxyMechanism AuthenticationMechanism = "ReverseProxy"

// Ensure the struct implements the interface.
var (
_ SingleSignOn = &ReverseProxy{}
Expand Down Expand Up @@ -104,7 +107,6 @@ func (r *ReverseProxy) newUser(req *http.Request) *models.User {
user := &models.User{
Name: username,
Email: email,
Passwd: username,
IsActive: true,
}
if err := models.CreateUser(user); err != nil {
Expand Down
1 change: 1 addition & 0 deletions modules/auth/sso/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func (s *Session) IsEnabled() bool {
func (s *Session) VerifyAuthData(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *models.User {
user := SessionUser(sess)
if user != nil {
store.GetData()["AuthenticationMechanism"] = ""
return user
}
return nil
Expand Down
9 changes: 1 addition & 8 deletions modules/auth/sso/sso.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,9 @@ var ssoMethods = []SingleSignOn{
&Basic{},
}

// AuthenticationMechanism represents possible authentication mechanisms
type AuthenticationMechanism string

const (
OAuth2Mechanism AuthenticationMechanism = "OAuth2"
TokenMechanism AuthenticationMechanism = "Token"
ReverseProxyMechanism AuthenticationMechanism = "ReverseProxy"
BasicAuthenticationMechanism AuthenticationMechanism = "Basic"
SSPIMechanism AuthenticationMechanism = "SSPI"
)

// The purpose of the following three function variables is to let the linter know that
// those functions are not dead code and are actually being used
var (
Expand Down
3 changes: 3 additions & 0 deletions modules/auth/sso/sspi_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ import (

const (
tplSignIn base.TplName = "user/auth/signin"

// SSPIMechanism represents authentication using SSPI
SSPIMechanism AuthenticationMechanism = "SSPI"
)

var (
Expand Down
8 changes: 4 additions & 4 deletions modules/lfs/locks.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func GetListLockHandler(ctx *context.Context) {
}
repository.MustOwner()

authenticated := authenticate(ctx, repository, rv.Authorization, false)
authenticated := authenticate(ctx, repository, rv.Authorization, true, false)
if !authenticated {
ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs")
ctx.JSON(http.StatusUnauthorized, api.LFSLockError{
Expand Down Expand Up @@ -169,7 +169,7 @@ func PostLockHandler(ctx *context.Context) {
}
repository.MustOwner()

authenticated := authenticate(ctx, repository, authorization, true)
authenticated := authenticate(ctx, repository, authorization, true, true)
if !authenticated {
ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs")
ctx.JSON(http.StatusUnauthorized, api.LFSLockError{
Expand Down Expand Up @@ -242,7 +242,7 @@ func VerifyLockHandler(ctx *context.Context) {
}
repository.MustOwner()

authenticated := authenticate(ctx, repository, authorization, true)
authenticated := authenticate(ctx, repository, authorization, true, true)
if !authenticated {
ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs")
ctx.JSON(http.StatusUnauthorized, api.LFSLockError{
Expand Down Expand Up @@ -313,7 +313,7 @@ func UnLockHandler(ctx *context.Context) {
}
repository.MustOwner()

authenticated := authenticate(ctx, repository, authorization, true)
authenticated := authenticate(ctx, repository, authorization, true, true)
if !authenticated {
ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs")
ctx.JSON(http.StatusUnauthorized, api.LFSLockError{
Expand Down
10 changes: 5 additions & 5 deletions modules/lfs/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ func getAuthenticatedRepoAndMeta(ctx *context.Context, rv *RequestVars, requireW
return nil, nil
}

if !authenticate(ctx, repository, rv.Authorization, requireWrite) {
if !authenticate(ctx, repository, rv.Authorization, false, requireWrite) {
requireAuth(ctx)
return nil, nil
}
Expand Down Expand Up @@ -268,7 +268,7 @@ func PostHandler(ctx *context.Context) {
return
}

if !authenticate(ctx, repository, rv.Authorization, true) {
if !authenticate(ctx, repository, rv.Authorization, false, true) {
requireAuth(ctx)
return
}
Expand Down Expand Up @@ -352,7 +352,7 @@ func BatchHandler(ctx *context.Context) {
requireWrite = true
}

if !authenticate(ctx, repository, object.Authorization, requireWrite) {
if !authenticate(ctx, repository, object.Authorization, false, requireWrite) {
requireAuth(ctx)
return
}
Expand Down Expand Up @@ -598,7 +598,7 @@ func logRequest(r *http.Request, status int) {

// authenticate uses the authorization string to determine whether
// or not to proceed. This server assumes an HTTP Basic auth format.
func authenticate(ctx *context.Context, repository *models.Repository, authorization string, requireWrite bool) bool {
func authenticate(ctx *context.Context, repository *models.Repository, authorization string, requireSigned, requireWrite bool) bool {
accessMode := models.AccessModeRead
if requireWrite {
accessMode = models.AccessModeWrite
Expand All @@ -612,7 +612,7 @@ func authenticate(ctx *context.Context, repository *models.Repository, authoriza
}

canRead := perm.CanAccess(accessMode, models.UnitTypeCode)
if canRead {
if canRead && (!requireSigned || ctx.IsSigned) {
return true
}

Expand Down
5 changes: 5 additions & 0 deletions routers/user/setting/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"strings"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth/sso"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
auth "code.gitea.io/gitea/modules/forms"
Expand Down Expand Up @@ -51,6 +52,10 @@ func HandleUsernameChange(ctx *context.Context, user *models.User, newName strin

// Check if user name has been changed
if user.LowerName != strings.ToLower(newName) {
if ctx.Data["AuthenticationMechanism"] == sso.ReverseProxyMechanism {
ctx.Flash.Error(ctx.Tr("form.username_change_not_local_user"))
return fmt.Errorf(ctx.Tr("form.username_change_not_local_user"))
}
if err := models.ChangeUserName(user, newName); err != nil {
switch {
case models.IsErrUserAlreadyExist(err):
Expand Down
4 changes: 2 additions & 2 deletions templates/user/settings/profile.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
<span class="text red hide" id="name-change-prompt"> {{.i18n.Tr "settings.change_username_prompt"}}</span>
<span class="text red hide" id="name-change-redirect-prompt"> {{.i18n.Tr "settings.change_username_redirect_prompt"}}</span>
</label>
<input id="username" name="name" value="{{.SignedUser.Name}}" data-name="{{.SignedUser.Name}}" autofocus required {{if not .SignedUser.IsLocal}}disabled{{end}}>
{{if not .SignedUser.IsLocal}}
<input id="username" name="name" value="{{.SignedUser.Name}}" data-name="{{.SignedUser.Name}}" autofocus required {{if or (not .SignedUser.IsLocal) (eq .AuthenticationMechanism "ReverseProxy")}}disabled{{end}}>
{{if or (not .SignedUser.IsLocal) (eq .AuthenticationMechanism "ReverseProxy")}}
<p class="help text blue">{{$.i18n.Tr "settings.password_username_disabled"}}</p>
{{end}}
</div>
Expand Down

0 comments on commit 41c8ccf

Please sign in to comment.