More work on auth
This commit is contained in:
parent
4cc6ecbc64
commit
e5941d6440
6 changed files with 168 additions and 44 deletions
|
@ -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"]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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{}
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
17
webapp/src/user.tsx
Normal 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}
|
Loading…
Reference in a new issue