You've already forked focalboard
mirror of
https://github.com/mattermost/focalboard.git
synced 2025-07-15 23:54:29 +02:00
Adding explict logout logic (#1895)
* Adding explict logout logic * Fixing golangci-lint errors
This commit is contained in:
@ -89,6 +89,7 @@ func (a *API) RegisterRoutes(r *mux.Router) {
|
|||||||
apiv1.HandleFunc("/users/{userID}/changepassword", a.sessionRequired(a.handleChangePassword)).Methods("POST")
|
apiv1.HandleFunc("/users/{userID}/changepassword", a.sessionRequired(a.handleChangePassword)).Methods("POST")
|
||||||
|
|
||||||
apiv1.HandleFunc("/login", a.handleLogin).Methods("POST")
|
apiv1.HandleFunc("/login", a.handleLogin).Methods("POST")
|
||||||
|
apiv1.HandleFunc("/logout", a.sessionRequired(a.handleLogout)).Methods("POST")
|
||||||
apiv1.HandleFunc("/register", a.handleRegister).Methods("POST")
|
apiv1.HandleFunc("/register", a.handleRegister).Methods("POST")
|
||||||
apiv1.HandleFunc("/clientConfig", a.getClientConfig).Methods("GET")
|
apiv1.HandleFunc("/clientConfig", a.getClientConfig).Methods("GET")
|
||||||
|
|
||||||
|
@ -211,6 +211,49 @@ func (a *API) handleLogin(w http.ResponseWriter, r *http.Request) {
|
|||||||
a.errorResponse(w, r.URL.Path, http.StatusBadRequest, "invalid login type", nil)
|
a.errorResponse(w, r.URL.Path, http.StatusBadRequest, "invalid login type", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *API) handleLogout(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// swagger:operation POST /api/v1/logout logout
|
||||||
|
//
|
||||||
|
// Logout user
|
||||||
|
//
|
||||||
|
// ---
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// security:
|
||||||
|
// - BearerAuth: []
|
||||||
|
// responses:
|
||||||
|
// '200':
|
||||||
|
// description: success
|
||||||
|
// '500':
|
||||||
|
// description: internal error
|
||||||
|
// schema:
|
||||||
|
// "$ref": "#/definitions/ErrorResponse"
|
||||||
|
|
||||||
|
if len(a.singleUserToken) > 0 {
|
||||||
|
// Not permitted in single-user mode
|
||||||
|
a.errorResponse(w, r.URL.Path, http.StatusUnauthorized, "not permitted in single-user mode", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := r.Context()
|
||||||
|
|
||||||
|
session := ctx.Value(sessionContextKey).(*model.Session)
|
||||||
|
|
||||||
|
auditRec := a.makeAuditRecord(r, "logout", audit.Fail)
|
||||||
|
defer a.audit.LogRecord(audit.LevelAuth, auditRec)
|
||||||
|
auditRec.AddMeta("userID", session.UserID)
|
||||||
|
|
||||||
|
if err := a.app.Logout(session.ID); err != nil {
|
||||||
|
a.errorResponse(w, r.URL.Path, http.StatusUnauthorized, "incorrect login", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
auditRec.AddMeta("sessionID", session.ID)
|
||||||
|
|
||||||
|
jsonStringResponse(w, http.StatusOK, "{}")
|
||||||
|
auditRec.Success()
|
||||||
|
}
|
||||||
|
|
||||||
func (a *API) handleRegister(w http.ResponseWriter, r *http.Request) {
|
func (a *API) handleRegister(w http.ResponseWriter, r *http.Request) {
|
||||||
// swagger:operation POST /api/v1/register register
|
// swagger:operation POST /api/v1/register register
|
||||||
//
|
//
|
||||||
|
@ -119,6 +119,18 @@ func (a *App) Login(username, email, password, mfaToken string) (string, error)
|
|||||||
return session.Token, nil
|
return session.Token, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logout invalidates the user session.
|
||||||
|
func (a *App) Logout(sessionID string) error {
|
||||||
|
err := a.store.DeleteSession(sessionID)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "unable to delete the session")
|
||||||
|
}
|
||||||
|
|
||||||
|
a.metrics.IncrementLogoutCount(1)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// RegisterUser creates a new user if the provided data is valid.
|
// RegisterUser creates a new user if the provided data is valid.
|
||||||
func (a *App) RegisterUser(username, email, password string) error {
|
func (a *App) RegisterUser(username, email, password string) error {
|
||||||
var user *model.User
|
var user *model.User
|
||||||
|
@ -31,6 +31,7 @@ type Metrics struct {
|
|||||||
startTime prometheus.Gauge
|
startTime prometheus.Gauge
|
||||||
|
|
||||||
loginCount prometheus.Counter
|
loginCount prometheus.Counter
|
||||||
|
logoutCount prometheus.Counter
|
||||||
loginFailCount prometheus.Counter
|
loginFailCount prometheus.Counter
|
||||||
|
|
||||||
blocksInsertedCount prometheus.Counter
|
blocksInsertedCount prometheus.Counter
|
||||||
@ -68,6 +69,15 @@ func NewMetrics(info InstanceInfo) *Metrics {
|
|||||||
})
|
})
|
||||||
m.registry.MustRegister(m.loginCount)
|
m.registry.MustRegister(m.loginCount)
|
||||||
|
|
||||||
|
m.logoutCount = prometheus.NewCounter(prometheus.CounterOpts{
|
||||||
|
Namespace: MetricsNamespace,
|
||||||
|
Subsystem: MetricsSubsystemSystem,
|
||||||
|
Name: "logout_total",
|
||||||
|
Help: "Total number of logouts.",
|
||||||
|
ConstLabels: additionalLabels,
|
||||||
|
})
|
||||||
|
m.registry.MustRegister(m.logoutCount)
|
||||||
|
|
||||||
m.loginFailCount = prometheus.NewCounter(prometheus.CounterOpts{
|
m.loginFailCount = prometheus.NewCounter(prometheus.CounterOpts{
|
||||||
Namespace: MetricsNamespace,
|
Namespace: MetricsNamespace,
|
||||||
Subsystem: MetricsSubsystemSystem,
|
Subsystem: MetricsSubsystemSystem,
|
||||||
@ -160,6 +170,12 @@ func (m *Metrics) IncrementLoginCount(num int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Metrics) IncrementLogoutCount(num int) {
|
||||||
|
if m != nil {
|
||||||
|
m.logoutCount.Add(float64(num))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Metrics) IncrementLoginFailCount(num int) {
|
func (m *Metrics) IncrementLoginFailCount(num int) {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
m.loginFailCount.Add(float64(num))
|
m.loginFailCount.Add(float64(num))
|
||||||
|
@ -56,7 +56,7 @@ const SidebarUserMenu = React.memo(() => {
|
|||||||
id='logout'
|
id='logout'
|
||||||
name={intl.formatMessage({id: 'Sidebar.logout', defaultMessage: 'Log out'})}
|
name={intl.formatMessage({id: 'Sidebar.logout', defaultMessage: 'Log out'})}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
octoClient.logout()
|
await octoClient.logout()
|
||||||
history.push('/login')
|
history.push('/login')
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -75,8 +75,18 @@ class OctoClient {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
logout(): void {
|
async logout(): Promise<boolean> {
|
||||||
|
const path = '/api/v1/logout'
|
||||||
|
const response = await fetch(this.getBaseURL() + path, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: this.headers(),
|
||||||
|
})
|
||||||
localStorage.removeItem('focalboardSessionId')
|
localStorage.removeItem('focalboardSessionId')
|
||||||
|
|
||||||
|
if (response.status !== 200) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
async getClientConfig(): Promise<ClientConfig | null> {
|
async getClientConfig(): Promise<ClientConfig | null> {
|
||||||
|
@ -20,8 +20,8 @@ const ErrorPage = React.memo(() => {
|
|||||||
<br/>
|
<br/>
|
||||||
<Button
|
<Button
|
||||||
filled={true}
|
filled={true}
|
||||||
onClick={() => {
|
onClick={async () => {
|
||||||
octoClient.logout()
|
await octoClient.logout()
|
||||||
window.location.href = '/login'
|
window.location.href = '/login'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
Reference in New Issue
Block a user