More work on auth

This commit is contained in:
Jesús Espino 2020-12-07 20:40:16 +01:00
parent 4cc6ecbc64
commit e5941d6440
6 changed files with 168 additions and 44 deletions

View file

@ -10,6 +10,7 @@ import (
"path/filepath"
"strconv"
"strings"
"time"
"github.com/gorilla/mux"
"github.com/mattermost/mattermost-octo-tasks/server/app"
@ -34,18 +35,21 @@ func (a *API) app() *app.App {
func (a *API) RegisterRoutes(r *mux.Router) {
r.HandleFunc("/api/v1/blocks", a.sessionRequired(a.handleGetBlocks)).Methods("GET")
r.HandleFunc("/api/v1/blocks", a.handlePostBlocks).Methods("POST")
r.HandleFunc("/api/v1/blocks/{blockID}", a.handleDeleteBlock).Methods("DELETE")
r.HandleFunc("/api/v1/blocks/{blockID}/subtree", a.handleGetSubTree).Methods("GET")
r.HandleFunc("/api/v1/blocks", a.sessionRequired(a.handlePostBlocks)).Methods("POST")
r.HandleFunc("/api/v1/blocks/{blockID}", a.sessionRequired(a.handleDeleteBlock)).Methods("DELETE")
r.HandleFunc("/api/v1/blocks/{blockID}/subtree", a.sessionRequired(a.handleGetSubTree)).Methods("GET")
r.HandleFunc("/api/v1/users/me", a.sessionRequired(a.handleGetMe)).Methods("GET")
r.HandleFunc("/api/v1/users/{userID}", a.sessionRequired(a.handleGetUser)).Methods("GET")
r.HandleFunc("/api/v1/login", a.handleLogin).Methods("POST")
r.HandleFunc("/api/v1/register", a.handleRegister).Methods("POST")
r.HandleFunc("/api/v1/files", a.handleUploadFile).Methods("POST")
r.HandleFunc("/files/{filename}", a.handleServeFile).Methods("GET")
r.HandleFunc("/api/v1/files", a.sessionRequired(a.handleUploadFile)).Methods("POST")
r.HandleFunc("/files/{filename}", a.sessionRequired(a.handleServeFile)).Methods("GET")
r.HandleFunc("/api/v1/blocks/export", a.handleExport).Methods("GET")
r.HandleFunc("/api/v1/blocks/import", a.handleImport).Methods("POST")
r.HandleFunc("/api/v1/blocks/export", a.sessionRequired(a.handleExport)).Methods("GET")
r.HandleFunc("/api/v1/blocks/import", a.sessionRequired(a.handleImport)).Methods("POST")
}
func (a *API) handleGetBlocks(w http.ResponseWriter, r *http.Request) {
@ -137,6 +141,65 @@ func (a *API) handlePostBlocks(w http.ResponseWriter, r *http.Request) {
jsonStringResponse(w, http.StatusOK, "{}")
}
func (a *API) handleGetUser(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
userID := vars["userID"]
user, err := a.app().GetUser(userID)
if err != nil {
log.Printf(`ERROR: %v`, r)
errorResponse(w, http.StatusInternalServerError, nil)
return
}
userData, err := json.Marshal(user)
if err != nil {
log.Printf(`ERROR: %v`, r)
errorResponse(w, http.StatusInternalServerError, nil)
return
}
jsonStringResponse(w, http.StatusOK, string(userData))
}
func (a *API) handleGetMe(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session := ctx.Value("session").(*model.Session)
var user *model.User
var err error
if session.UserID == "single-user" {
now := time.Now().Unix()
user = &model.User{
ID: "single-user",
Username: "single-user",
Email: "single-user",
CreateAt: now,
UpdateAt: now,
}
} else {
user, err = a.app().GetUser(session.UserID)
if err != nil {
log.Printf(`ERROR: %v`, r)
errorResponse(w, http.StatusInternalServerError, nil)
return
}
}
userData, err := json.Marshal(user)
if err != nil {
log.Printf(`ERROR: %v`, r)
errorResponse(w, http.StatusInternalServerError, nil)
return
}
jsonStringResponse(w, http.StatusOK, string(userData))
}
func (a *API) handleDeleteBlock(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
blockID := vars["blockID"]

View file

@ -23,6 +23,15 @@ func (a *App) GetSession(token string) (*model.Session, error) {
return session, nil
}
// GetUser Get an existing active user by id
func (a *App) GetUser(ID string) (*model.User, error) {
user, err := a.store.GetUserById(ID)
if err != nil {
return nil, errors.Wrap(err, "unable to get the session for the token")
}
return user, nil
}
// Login create a new user session if the authentication data is valid
func (a *App) Login(username string, email string, password string, mfaToken string) (string, error) {
var user *model.User

View file

@ -13,6 +13,7 @@ func (s *SQLStore) getUserByCondition(condition sq.Eq) (*model.User, error) {
query := s.getQueryBuilder().
Select("id", "username", "email", "password", "mfa_secret", "auth_service", "auth_data", "props", "create_at", "update_at", "delete_at").
From("users").
Where(sq.Eq{"delete_at": 0}).
Where(condition)
row := query.QueryRow()
user := model.User{}

View file

@ -1,6 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useState} from 'react'
import React from 'react'
import {IntlProvider} from 'react-intl'
import {
@ -11,6 +11,7 @@ import {
} from 'react-router-dom'
import client from './octoClient'
import {IUser, UserContext} from './user'
import {getCurrentLanguage, getMessages, storeLanguage} from './i18n'
@ -20,40 +21,65 @@ import LoginPage from './pages/loginPage'
import RegisterPage from './pages/registerPage'
import BoardPage from './pages/boardPage'
export default function App(): JSX.Element {
const [language, setLanguage] = useState(getCurrentLanguage())
const setAndStoreLanguage = (lang: string) => {
storeLanguage(lang)
setLanguage(lang)
}
return (
<IntlProvider
locale={language}
messages={getMessages(language)}
>
<FlashMessages milliseconds={2000}/>
<Router>
<div id='frame'>
<div id='main'>
<Switch>
<Route path='/login'>
<LoginPage/>
</Route>
<Route path='/register'>
<RegisterPage/>
</Route>
<Route path='/'>
{!client.token && <Redirect to='/login'/>}
<BoardPage setLanguage={setAndStoreLanguage}/>
</Route>
<Route path='/board'>
{!client.token && <Redirect to='/login'/>}
<BoardPage setLanguage={setAndStoreLanguage}/>
</Route>
</Switch>
</div>
</div>
</Router>
</IntlProvider>
)
type State = {
language: string,
user: IUser|null,
initialLoad: boolean,
}
export default class App extends React.PureComponent<unknown, State> {
constructor(props: unknown) {
super(props)
this.state = {
language: getCurrentLanguage(),
user: null,
initialLoad: false,
}
}
public componentDidMount(): void {
client.getMe().then((user: IUser|null) => {
this.setState({user, initialLoad: true})
})
}
setAndStoreLanguage = (lang: string): void => {
storeLanguage(lang)
this.setState({language: lang})
}
public render(): JSX.Element {
return (
<IntlProvider
locale={this.state.language}
messages={getMessages(this.state.language)}
>
<UserContext.Provider value={this.state.user}>
<FlashMessages milliseconds={2000}/>
<Router>
<div id='frame'>
<div id='main'>
<Switch>
<Route path='/login'>
<LoginPage/>
</Route>
<Route path='/register'>
<RegisterPage/>
</Route>
<Route path='/'>
{this.state.initialLoad && !this.state.user && <Redirect to='login'/>}
<BoardPage setLanguage={this.setAndStoreLanguage}/>
</Route>
<Route path='/board'>
{this.state.initialLoad && !this.state.user && <Redirect to='login'/>}
<BoardPage setLanguage={this.setAndStoreLanguage}/>
</Route>
</Switch>
</div>
</div>
</Router>
</UserContext.Provider>
</IntlProvider>
)
}
}

View file

@ -1,6 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {IBlock, IMutableBlock} from './blocks/block'
import {IUser} from './user'
import {Utils} from './utils'
//
@ -58,6 +59,13 @@ class OctoClient {
}
}
async getMe(): Promise<IUser|null> {
const path = '/api/v1/users/me'
const response = await fetch(this.serverUrl + path, {headers: this.headers()})
const user = (await response.json()) as IUser || null
return user
}
async getSubtree(rootId?: string, levels = 2): Promise<IBlock[]> {
const path = `/api/v1/blocks/${rootId}/subtree?l=${levels}`
const response = await fetch(this.serverUrl + path, {headers: this.headers()})

17
webapp/src/user.tsx Normal file
View file

@ -0,0 +1,17 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react'
const UserContext = React.createContext(null as IUser|null)
interface IUser {
id: string,
username: string,
email: string,
props: Record<string, any>,
createAt: number,
updateAt: number,
}
export {IUser, UserContext}