mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2025-06-05 21:59:39 +02:00
[feature] add authorization to the already-existing authentication (#365)
* add ensureUserIsAuthorizedOrRedirect to /oauth/authorize * adding authorization (email confirm, account approve, etc) to TokenCheck * revert un-needed changes to signin.go * oops what happened here * error css * add account.SuspendedAt check * remove redundant checks from oauth util Authed function * wip tests * tests passing * stop stripping useful information from ErrAlreadyExists * that feeling of scraping the dryer LINT off the screen * oops I didn't mean to get rid of this NewTestRouter function * make tests work with recorder * re-add ConfigureTemplatesWithGin to handle template path err Co-authored-by: tsmethurst <tobi.smethurst@protonmail.com>
This commit is contained in:
@ -44,7 +44,7 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
|
||||
s := sessions.Default(c)
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.HTMLAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
c.HTML(http.StatusNotAcceptable, "error.tmpl", gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
@ -57,7 +57,7 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
|
||||
if err := c.Bind(form); err != nil {
|
||||
l.Debugf("invalid auth form: %s", err)
|
||||
m.clearSession(s)
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
c.HTML(http.StatusBadRequest, "error.tmpl", gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
l.Debugf("parsed auth form: %+v", form)
|
||||
@ -65,7 +65,7 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
|
||||
if err := extractAuthForm(s, form); err != nil {
|
||||
l.Debugf(fmt.Sprintf("error parsing form at /oauth/authorize: %s", err))
|
||||
m.clearSession(s)
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
c.HTML(http.StatusBadRequest, "error.tmpl", gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.Redirect(http.StatusSeeOther, AuthSignInPath)
|
||||
@ -75,28 +75,33 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
|
||||
// We can use the client_id on the session to retrieve info about the app associated with the client_id
|
||||
clientID, ok := s.Get(sessionClientID).(string)
|
||||
if !ok || clientID == "" {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "no client_id found in session"})
|
||||
c.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{"error": "no client_id found in session"})
|
||||
return
|
||||
}
|
||||
app := >smodel.Application{}
|
||||
if err := m.db.GetWhere(c.Request.Context(), []db.Where{{Key: sessionClientID, Value: clientID}}, app); err != nil {
|
||||
m.clearSession(s)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("no application found for client id %s", clientID)})
|
||||
c.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{
|
||||
"error": fmt.Sprintf("no application found for client id %s", clientID),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// we can also use the userid of the user to fetch their username from the db to greet them nicely <3
|
||||
// redirect the user if they have not confirmed their email yet, thier account has not been approved yet,
|
||||
// or thier account has been disabled.
|
||||
user := >smodel.User{}
|
||||
if err := m.db.GetByID(c.Request.Context(), userID, user); err != nil {
|
||||
m.clearSession(s)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
c.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
acct, err := m.db.GetAccountByID(c.Request.Context(), user.AccountID)
|
||||
if err != nil {
|
||||
m.clearSession(s)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
c.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if !ensureUserIsAuthorizedOrRedirect(c, user, acct) {
|
||||
return
|
||||
}
|
||||
|
||||
@ -104,13 +109,13 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
|
||||
redirect, ok := s.Get(sessionRedirectURI).(string)
|
||||
if !ok || redirect == "" {
|
||||
m.clearSession(s)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "no redirect_uri found in session"})
|
||||
c.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{"error": "no redirect_uri found in session"})
|
||||
return
|
||||
}
|
||||
scope, ok := s.Get(sessionScope).(string)
|
||||
if !ok || scope == "" {
|
||||
m.clearSession(s)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "no scope found in session"})
|
||||
c.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{"error": "no scope found in session"})
|
||||
return
|
||||
}
|
||||
|
||||
@ -170,10 +175,28 @@ func (m *Module) AuthorizePOSTHandler(c *gin.Context) {
|
||||
errs = append(errs, "session missing userid")
|
||||
}
|
||||
|
||||
// redirect the user if they have not confirmed their email yet, thier account has not been approved yet,
|
||||
// or thier account has been disabled.
|
||||
user := >smodel.User{}
|
||||
if err := m.db.GetByID(c.Request.Context(), userID, user); err != nil {
|
||||
m.clearSession(s)
|
||||
c.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
acct, err := m.db.GetAccountByID(c.Request.Context(), user.AccountID)
|
||||
if err != nil {
|
||||
m.clearSession(s)
|
||||
c.HTML(http.StatusInternalServerError, "error.tmpl", gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if !ensureUserIsAuthorizedOrRedirect(c, user, acct) {
|
||||
return
|
||||
}
|
||||
|
||||
m.clearSession(s)
|
||||
|
||||
if len(errs) != 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": strings.Join(errs, ": ")})
|
||||
c.HTML(http.StatusBadRequest, "error.tmpl", gin.H{"error": strings.Join(errs, ": ")})
|
||||
return
|
||||
}
|
||||
|
||||
@ -190,7 +213,7 @@ func (m *Module) AuthorizePOSTHandler(c *gin.Context) {
|
||||
|
||||
// and proceed with authorization using the oauth2 library
|
||||
if err := m.server.HandleAuthorizeRequest(c.Writer, c.Request); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
c.HTML(http.StatusBadRequest, "error.tmpl", gin.H{"error": err.Error()})
|
||||
}
|
||||
}
|
||||
|
||||
@ -216,3 +239,27 @@ func extractAuthForm(s sessions.Session, form *model.OAuthAuthorize) error {
|
||||
s.Set(sessionState, uuid.NewString())
|
||||
return s.Save()
|
||||
}
|
||||
|
||||
func ensureUserIsAuthorizedOrRedirect(ctx *gin.Context, user *gtsmodel.User, account *gtsmodel.Account) bool {
|
||||
if user.ConfirmedAt.IsZero() {
|
||||
ctx.Redirect(http.StatusSeeOther, CheckYourEmailPath)
|
||||
return false
|
||||
}
|
||||
|
||||
if !user.Approved {
|
||||
ctx.Redirect(http.StatusSeeOther, WaitForApprovalPath)
|
||||
return false
|
||||
}
|
||||
|
||||
if user.Disabled {
|
||||
ctx.Redirect(http.StatusSeeOther, AccountDisabledPath)
|
||||
return false
|
||||
}
|
||||
|
||||
if !account.SuspendedAt.IsZero() {
|
||||
ctx.Redirect(http.StatusSeeOther, AccountDisabledPath)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
Reference in New Issue
Block a user