mirror of
https://github.com/gravitational/teleport
synced 2024-10-22 10:13:21 +00:00
handle 403 and display local and node ip in archived list
This commit is contained in:
parent
a86ec3bdd8
commit
0ef1d46d58
File diff suppressed because it is too large
Load diff
|
@ -3,12 +3,12 @@ webpackJsonp([1],{
|
|||
/***/ 0:
|
||||
/***/ function(module, exports, __webpack_require__) {
|
||||
|
||||
module.exports = __webpack_require__(460);
|
||||
module.exports = __webpack_require__(461);
|
||||
|
||||
|
||||
/***/ },
|
||||
|
||||
/***/ 460:
|
||||
/***/ 461:
|
||||
/***/ function(module, exports) {
|
||||
|
||||
// removed by extract-text-webpack-plugin
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4
web/dist/index.html
vendored
4
web/dist/index.html
vendored
|
@ -6,9 +6,9 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Teleport by Gravitational</title>
|
||||
<script src="/web/config.js"></script>
|
||||
<link rel="shortcut icon" href="/web/app/favicon.ico"><link href="/web/app/vendor.3cef2e9ca8fbd0402d43e14af05c3521.css" rel="stylesheet"></head>
|
||||
<link rel="shortcut icon" href="/web/app/favicon.ico"><link href="/web/app/vendor.12cdb3da5b237d375fb2d5687e83bcdf.css" rel="stylesheet"></head>
|
||||
<body class="grv">
|
||||
<div id="app"></div>
|
||||
<div id="bearer_token" style="display: none;">{{.Session}}</div>
|
||||
<script type="text/javascript" src="/web/app/vendor.dfe7f8b338cfdef83b06.js"></script><script type="text/javascript" src="/web/app/styles.dfe7f8b338cfdef83b06.js"></script><script type="text/javascript" src="/web/app/app.dfe7f8b338cfdef83b06.js"></script></body>
|
||||
<script type="text/javascript" src="/web/app/vendor.e2e926d587c1591b34f7.js"></script><script type="text/javascript" src="/web/app/styles.e2e926d587c1591b34f7.js"></script><script type="text/javascript" src="/web/app/app.e2e926d587c1591b34f7.js"></script></body>
|
||||
</html>
|
||||
|
|
|
@ -18,8 +18,10 @@ var React = require('react');
|
|||
var NavLeftBar = require('./navLeftBar');
|
||||
var reactor = require('app/reactor');
|
||||
var {actions, getters} = require('app/modules/app');
|
||||
|
||||
var SelectNodeDialog = require('./selectNodeDialog.jsx');
|
||||
var NotificationHost = require('./notificationHost.jsx');
|
||||
var Timer = require('./timer.jsx');
|
||||
|
||||
var App = React.createClass({
|
||||
|
||||
|
@ -35,13 +37,14 @@ var App = React.createClass({
|
|||
actions.initApp();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
if(this.state.app.isInitializing){
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="grv-tlpt grv-flex grv-flex-row">
|
||||
<Timer onTimeout={actions.checkIfUserLoggedIn}/>
|
||||
<SelectNodeDialog/>
|
||||
<NotificationHost/>
|
||||
{this.props.CurrentSessionHost}
|
||||
|
|
|
@ -18,21 +18,12 @@ var React = require('react');
|
|||
var reactor = require('app/reactor');
|
||||
var userGetters = require('app/modules/user/getters');
|
||||
var nodeGetters = require('app/modules/nodes/getters');
|
||||
var {fetchNodes} = require('app/modules/nodes/actions');
|
||||
var NodeList = require('./nodeList.jsx');
|
||||
|
||||
var Nodes = React.createClass({
|
||||
|
||||
mixins: [reactor.ReactMixin],
|
||||
|
||||
componentDidMount(){
|
||||
this.refreshInterval = setInterval(fetchNodes, 2500);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
clearInterval(this.refreshInterval);
|
||||
},
|
||||
|
||||
getDataBindings() {
|
||||
return {
|
||||
nodeRecords: nodeGetters.nodeListView,
|
||||
|
@ -40,7 +31,7 @@ var Nodes = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
var nodeRecords = this.state.nodeRecords;
|
||||
var logins = this.state.user.logins;
|
||||
return ( <NodeList nodeRecords={nodeRecords} logins={logins}/> );
|
||||
|
|
|
@ -131,10 +131,10 @@ var NodeList = React.createClass({
|
|||
|
||||
return (
|
||||
<div className="grv-nodes grv-page">
|
||||
<div className="grv-flex grv-header">
|
||||
<div className="grv-flex grv-header m-t-md">
|
||||
<div className="grv-flex-column"></div>
|
||||
<div className="grv-flex-column">
|
||||
<h1> Nodes </h1>
|
||||
<h2 className="text-center no-margins"> Nodes </h2>
|
||||
</div>
|
||||
<div className="grv-flex-column">
|
||||
<InputSearch value={this.filter} onChange={this.onFilterChange}/>
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
Copyright 2015 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
|
@ -17,25 +17,16 @@ limitations under the License.
|
|||
var React = require('react');
|
||||
var {Table, Column, Cell, TextCell, EmptyIndicator} = require('app/components/table.jsx');
|
||||
var {ButtonCell, UsersCell, NodeCell, DateCreatedCell} = require('./listItems');
|
||||
var {fetchActiveSessions} = require('app/modules/sessions/actions');
|
||||
|
||||
var ActiveSessionList = React.createClass({
|
||||
|
||||
componentWillMount(){
|
||||
fetchActiveSessions();
|
||||
this.refreshInterval = setInterval(fetchActiveSessions, 2500);
|
||||
},
|
||||
|
||||
componentWillUnmount(){
|
||||
clearInterval(this.refreshInterval);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
let data = this.props.data.filter(item => item.active);
|
||||
|
||||
return (
|
||||
<div className="grv-sessions-active">
|
||||
<div className="grv-header">
|
||||
<h1> Active Sessions </h1>
|
||||
<h2 className="text-center"> Active Sessions </h2>
|
||||
</div>
|
||||
<div className="grv-content">
|
||||
{data.length === 0 ? <EmptyIndicator text="You have no active sessions."/> :
|
||||
|
|
|
@ -16,10 +16,13 @@ limitations under the License.
|
|||
|
||||
var React = require('react');
|
||||
var reactor = require('app/reactor');
|
||||
var {fetchActiveSessions} = require('app/modules/sessions/actions');
|
||||
var { fetchStoredSession } = require('app/modules/storedSessionsFilter/actions');
|
||||
var {sessionsView} = require('app/modules/sessions/getters');
|
||||
var {filter} = require('app/modules/storedSessionsFilter/getters');
|
||||
var StoredSessionList = require('./storedSessionList.jsx');
|
||||
var ActiveSessionList = require('./activeSessionList.jsx');
|
||||
var Timer = require('./../timer.jsx');
|
||||
|
||||
var Sessions = React.createClass({
|
||||
mixins: [reactor.ReactMixin],
|
||||
|
@ -31,10 +34,16 @@ var Sessions = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
refresh(){
|
||||
fetchStoredSession();
|
||||
fetchActiveSessions();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
let {data, storedSessionsFilter} = this.state;
|
||||
return (
|
||||
<div className="grv-sessions grv-page">
|
||||
<Timer onTimeout={this.refresh} />
|
||||
<ActiveSessionList data={data}/>
|
||||
<hr className="grv-divider"/>
|
||||
<StoredSessionList data={data} filter={storedSessionsFilter}/>
|
||||
|
|
|
@ -14,33 +14,25 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
var React = require('react');
|
||||
var {actions} = require('app/modules/storedSessionsFilter');
|
||||
var InputSearch = require('./../inputSearch.jsx');
|
||||
var {Table, Column, Cell, TextCell, SortHeaderCell, SortTypes, EmptyIndicator} = require('app/components/table.jsx');
|
||||
var {ButtonCell, SingleUserCell, DateCreatedCell} = require('./listItems');
|
||||
var {DateRangePicker} = require('./../datePicker.jsx');
|
||||
var moment = require('moment');
|
||||
var {isMatch} = require('app/common/objectUtils');
|
||||
var _ = require('_');
|
||||
var {displayDateFormat} = require('app/config');
|
||||
var React = require('react');
|
||||
var moment = require('moment');
|
||||
var InputSearch = require('./../inputSearch.jsx');
|
||||
var { isMatch } = require('app/common/objectUtils');
|
||||
var { displayDateFormat} = require('app/config');
|
||||
var { actions } = require('app/modules/storedSessionsFilter');
|
||||
var { Table, Column, Cell, TextCell, SortHeaderCell, SortTypes, EmptyIndicator } = require('app/components/table.jsx');
|
||||
var { ButtonCell, SingleUserCell, DateCreatedCell } = require('./listItems');
|
||||
var { DateRangePicker } = require('./../datePicker.jsx');
|
||||
|
||||
|
||||
var ArchivedSessions = React.createClass({
|
||||
|
||||
getInitialState(){
|
||||
this.searchableProps = ['serverIp', 'created', 'sid', 'login'];
|
||||
this.searchableProps = ['clientIp', 'nodeIp', 'created', 'sid', 'login'];
|
||||
return { filter: '', colSortDirs: {created: 'ASC'}};
|
||||
},
|
||||
|
||||
componentWillMount(){
|
||||
setTimeout(actions.fetch, 0);
|
||||
this.refreshInterval = setInterval(actions.fetch, 2500);
|
||||
},
|
||||
|
||||
componentWillUnmount(){
|
||||
clearInterval(this.refreshInterval);
|
||||
},
|
||||
|
||||
onFilterChange(value){
|
||||
this.state.filter = value;
|
||||
this.setState(this.state);
|
||||
|
@ -85,7 +77,7 @@ var ArchivedSessions = React.createClass({
|
|||
return sorted;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
render() {
|
||||
let { start, end } = this.props.filter;
|
||||
let data = this.props.data.filter(
|
||||
item => !item.active && moment(item.created).isBetween(start, end));
|
||||
|
@ -95,10 +87,10 @@ var ArchivedSessions = React.createClass({
|
|||
return (
|
||||
<div className="grv-sessions-stored">
|
||||
<div className="grv-header">
|
||||
<div className="grv-flex">
|
||||
<div className="grv-flex m-b-md">
|
||||
<div className="grv-flex-column"></div>
|
||||
<div className="grv-flex-column">
|
||||
<h1> Archived Sessions </h1>
|
||||
<h2 className="text-center"> Archived Sessions </h2>
|
||||
</div>
|
||||
<div className="grv-flex-column">
|
||||
<InputSearch value={this.filter} onChange={this.onFilterChange}/>
|
||||
|
@ -114,22 +106,31 @@ var ArchivedSessions = React.createClass({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grv-content">
|
||||
{data.length === 0 ? <EmptyIndicator text="No matching archived sessions found."/> :
|
||||
<div className="">
|
||||
<Table rowCount={data.length} className="table-striped">
|
||||
<Column
|
||||
columnKey="sid"
|
||||
header={<Cell> Session ID </Cell> }
|
||||
cell={<TextCell data={data}/> }
|
||||
/>
|
||||
<Column
|
||||
header={<Cell/>}
|
||||
cell={
|
||||
<ButtonCell data={data} />
|
||||
}
|
||||
/>
|
||||
<Column
|
||||
columnKey="nodeIp"
|
||||
header={<Cell> Node IP </Cell> }
|
||||
cell={<TextCell data={data} /> }
|
||||
/>
|
||||
<Column
|
||||
columnKey="clientIp"
|
||||
header={<Cell> Client IP </Cell> }
|
||||
cell={<TextCell data={data} /> }
|
||||
/>
|
||||
<Column
|
||||
columnKey="sid"
|
||||
header={<Cell> Session ID </Cell> }
|
||||
cell={<TextCell data={data}/> }
|
||||
/>
|
||||
<Column
|
||||
columnKey="created"
|
||||
header={
|
||||
|
|
41
web/src/app/components/timer.jsx
Normal file
41
web/src/app/components/timer.jsx
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
Copyright 2015 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
var React = require('react');
|
||||
|
||||
let Timer = React.createClass({
|
||||
|
||||
shouldComponentUpdate(){
|
||||
return false;
|
||||
},
|
||||
|
||||
componentWillMount(){
|
||||
let { onTimeout, interval=2500 } = this.props;
|
||||
onTimeout();
|
||||
this.refreshInterval = setInterval(onTimeout, interval);
|
||||
},
|
||||
|
||||
componentWillUnmount(){
|
||||
clearInterval(this.refreshInterval);
|
||||
},
|
||||
|
||||
render() {
|
||||
return null
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
module.exports = Timer;
|
|
@ -15,8 +15,9 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
var reactor = require('app/reactor');
|
||||
var {fetchActiveSessions} = require('./../sessions/actions');
|
||||
var {fetchNodes} = require('./../nodes/actions');
|
||||
var { fetchActiveSessions } = require('./../sessions/actions');
|
||||
var { fetchNodes } = require('./../nodes/actions');
|
||||
var { logout } = require('app/services/auth');
|
||||
var $ = require('jQuery');
|
||||
|
||||
const { TLPT_APP_INIT, TLPT_APP_FAILED, TLPT_APP_READY } = require('./actionTypes');
|
||||
|
@ -24,12 +25,24 @@ const { TLPT_APP_INIT, TLPT_APP_FAILED, TLPT_APP_READY } = require('./actionType
|
|||
const actions = {
|
||||
|
||||
initApp() {
|
||||
reactor.dispatch(TLPT_APP_INIT);
|
||||
reactor.dispatch(TLPT_APP_INIT);
|
||||
actions.fetchNodesAndSessions()
|
||||
.done(()=> reactor.dispatch(TLPT_APP_READY) )
|
||||
.fail(()=> reactor.dispatch(TLPT_APP_FAILED) );
|
||||
},
|
||||
|
||||
checkIfUserLoggedIn(){
|
||||
/*
|
||||
* lets query for nodes as a checker for a valid user session, in case of 403
|
||||
* make a redirect to a login page (in case if a server got restarted).
|
||||
*/
|
||||
fetchNodes().fail(err => {
|
||||
if(err.status == 403){
|
||||
logout();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
fetchNodesAndSessions() {
|
||||
return $.when(fetchNodes(), fetchActiveSessions());
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ const logger = require('app/common/logger').create('Modules/Nodes');
|
|||
|
||||
export default {
|
||||
fetchNodes(){
|
||||
api.get(cfg.api.nodesPath).done((data=[])=>{
|
||||
return api.get(cfg.api.nodesPath).done((data=[])=>{
|
||||
var nodeArray = data.nodes.map(item=>item.node);
|
||||
reactor.dispatch(TLPT_NODES_RECEIVE, nodeArray);
|
||||
}).fail((err)=>{
|
||||
|
|
|
@ -19,12 +19,12 @@ var { TLPT_NOTIFICATIONS_ADD } = require('./actionTypes');
|
|||
|
||||
export default {
|
||||
|
||||
showError(text, title='ERROR'){
|
||||
dispatch({isError: true, text: text, title});
|
||||
showError(title='Error'){
|
||||
dispatch({isError: true, title});
|
||||
},
|
||||
|
||||
showSuccess(text, title='SUCCESS'){
|
||||
dispatch({isSuccess:true, text: text, title});
|
||||
showSuccess(title='SUCCESS'){
|
||||
dispatch({isSuccess:true, title});
|
||||
},
|
||||
|
||||
showInfo(text, title='INFO'){
|
||||
|
|
|
@ -60,7 +60,7 @@ function createView(session){
|
|||
var parties = reactor.evaluate(partiesBySessionId(sid));
|
||||
|
||||
if(parties.length > 0){
|
||||
serverIp = parties[0].serverIp;
|
||||
serverIp = parties[0].serverIp;
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -68,6 +68,8 @@ function createView(session){
|
|||
sessionUrl: cfg.getActiveSessionRouteUrl(sid),
|
||||
serverIp,
|
||||
serverId: session.get('server_id'),
|
||||
clientIp: session.get('clientIp'),
|
||||
nodeIp: session.get('nodeIp'),
|
||||
active: session.get('active'),
|
||||
created: session.get('created'),
|
||||
lastActive: session.get('last_active'),
|
||||
|
@ -79,7 +81,6 @@ function createView(session){
|
|||
}
|
||||
|
||||
export default {
|
||||
partiesBySessionId,
|
||||
sessionsByServer,
|
||||
sessionsView,
|
||||
sessionViewById,
|
||||
|
|
|
@ -44,6 +44,11 @@ function removeStoredSessions(state){
|
|||
});
|
||||
}
|
||||
|
||||
function parseIp(ip){
|
||||
ip = ip || '';
|
||||
return ip.split(':')[0];
|
||||
}
|
||||
|
||||
function updateSessionWithEvents(state, events){
|
||||
return state.withMutations(state => {
|
||||
events.forEach(item=>{
|
||||
|
@ -57,8 +62,12 @@ function updateSessionWithEvents(state, events){
|
|||
|
||||
// check if record already exists
|
||||
let session = state.get(item.sid);
|
||||
if(!session){
|
||||
session = { id: item.sid };
|
||||
if(!session){
|
||||
session = {
|
||||
nodeIp: parseIp(item['addr.local']),
|
||||
clientIp: parseIp(item['addr.remote']),
|
||||
id: item.sid
|
||||
};
|
||||
}else{
|
||||
session = session.toJS();
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ const { TLPT_SESSINS_REMOVE_STORED } = require('./../sessions/actionTypes');
|
|||
|
||||
const actions = {
|
||||
|
||||
fetch(){
|
||||
fetchStoredSession(){
|
||||
let { start, end } = reactor.evaluate(filter);
|
||||
_fetch(start, end);
|
||||
},
|
||||
|
|
|
@ -22,5 +22,3 @@ limitations under the License.
|
|||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@import url("https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700");
|
||||
|
|
|
@ -23,11 +23,11 @@ limitations under the License.
|
|||
text-align: initial;
|
||||
position: relative;
|
||||
margin: 15px 0 0 0;
|
||||
padding: 0 0 0 70px;
|
||||
padding: 0 0 0 60px;
|
||||
|
||||
.grv-icon-google-auth{
|
||||
position: absolute;
|
||||
left: 15px;
|
||||
left: 0px;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
background-size: contain;
|
||||
|
|
|
@ -28,7 +28,7 @@ limitations under the License.
|
|||
}
|
||||
|
||||
.grv-content{
|
||||
padding: 15px;
|
||||
padding: 10px;
|
||||
width: 600px;
|
||||
z-index: 100;
|
||||
margin: 0 auto;
|
||||
|
|
|
@ -26,7 +26,7 @@ limitations under the License.
|
|||
}
|
||||
|
||||
.grv-content {
|
||||
padding: 15px;
|
||||
padding: 10px;
|
||||
max-width: 350px;
|
||||
z-index: 100;
|
||||
margin: 10px auto 0;
|
||||
|
@ -48,10 +48,10 @@ limitations under the License.
|
|||
text-align: initial;
|
||||
position: relative;
|
||||
margin: 15px 0 0;
|
||||
padding: 0 0 0 70px;
|
||||
padding: 0 0 0 60px;
|
||||
.fa-question {
|
||||
position: absolute;
|
||||
left: 20px;
|
||||
left: 7px;
|
||||
font-size: 40px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,16 +16,8 @@ limitations under the License.
|
|||
|
||||
.grv-nodes{
|
||||
|
||||
h1{
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.grv-search{
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.grv-header{
|
||||
align-items: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.grv-nodes-table{
|
||||
|
|
|
@ -37,10 +37,13 @@ limitations under the License.
|
|||
|
||||
.grv-sessions-stored{
|
||||
flex: 1;
|
||||
|
||||
.grv-header{
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
h1{
|
||||
margin-bottom: 30px;
|
||||
|
||||
h2{
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.grv-flex{
|
||||
|
@ -50,7 +53,6 @@ limitations under the License.
|
|||
.grv-datepicker {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.grv-footer{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
body {
|
||||
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
background-color: #2f4050;
|
||||
font-size: 13px;
|
||||
font-size: 14px;
|
||||
color: $text-color;
|
||||
overflow-x: hidden;
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
box-shadow: 0 0 3px rgba(86, 96, 117, 0.7);
|
||||
display: none;
|
||||
float: left;
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
left: 0;
|
||||
list-style: none outside none;
|
||||
padding: 0;
|
||||
|
@ -142,7 +142,7 @@
|
|||
}
|
||||
|
||||
.nav-header {
|
||||
padding: 33px 25px;
|
||||
padding: 33px 25px;
|
||||
}
|
||||
|
||||
.pace-done .nav-header {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Basic Colors
|
||||
$navy: #1ab394; // Primary color
|
||||
$navy: #4e988b; // Primary color
|
||||
$dark-gray: #c2c2c2; // Default color
|
||||
$blue: #1c84c6; // Success color
|
||||
$lazur: #23c6c8; // Info color
|
||||
|
|
Loading…
Reference in a new issue