You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by an...@apache.org on 2016/09/08 04:46:28 UTC
[11/50] [abbrv] ignite git commit: IGNITE-2388 Implemented ACL
services and userbar modification on ACL.
IGNITE-2388 Implemented ACL services and userbar modification on ACL.
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/f1cdb871
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/f1cdb871
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/f1cdb871
Branch: refs/heads/ignite-3629
Commit: f1cdb871884c69c4d1c3a1073e694dccc3fcb634
Parents: 8e844d2
Author: Maxim Afanasiev <ma...@gridgain.com>
Authored: Wed Aug 24 16:36:27 2016 +0700
Committer: Andrey Novikov <an...@apache.org>
Committed: Wed Aug 24 16:36:27 2016 +0700
----------------------------------------------------------------------
modules/web-console/frontend/app/app.js | 5 +--
.../app/modules/navbar/userbar.directive.js | 8 ++---
.../frontend/app/modules/states/admin.state.js | 3 +-
.../app/modules/states/configuration.state.js | 10 ++++--
.../frontend/app/modules/states/errors.state.js | 37 +++++++++++++++++++
.../frontend/app/modules/states/logout.state.js | 7 ++--
.../app/modules/states/password.state.js | 4 ++-
.../app/modules/states/profile.state.js | 3 +-
.../frontend/app/modules/states/signin.state.js | 26 +++++---------
.../app/modules/user/AclRoute.provider.js | 38 ++++++++++++++++++++
.../frontend/app/modules/user/permissions.js | 28 +++++++++++++++
.../frontend/app/modules/user/user.module.js | 29 +++++++++++++--
modules/web-console/frontend/app/vendor.js | 1 +
modules/web-console/frontend/package.json | 1 +
.../public/stylesheets/blocks/error.scss | 31 ++++++++++++++++
.../frontend/public/stylesheets/style.scss | 1 +
modules/web-console/frontend/views/403.jade | 30 ++++++++++++++++
modules/web-console/frontend/views/404.jade | 30 ++++++++++++++++
18 files changed, 257 insertions(+), 35 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ignite/blob/f1cdb871/modules/web-console/frontend/app/app.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/app.js b/modules/web-console/frontend/app/app.js
index 7340d7b..549081f 100644
--- a/modules/web-console/frontend/app/app.js
+++ b/modules/web-console/frontend/app/app.js
@@ -34,6 +34,7 @@ import './modules/states/password.state';
import './modules/states/configuration.state';
import './modules/states/profile.state';
import './modules/states/admin.state';
+import './modules/states/errors.state';
// ignite:modules
import './modules/user/user.module';
@@ -158,6 +159,7 @@ angular
'ignite-console.states.configuration',
'ignite-console.states.profile',
'ignite-console.states.admin',
+ 'ignite-console.states.errors',
// Common modules.
'ignite-console.dialog',
'ignite-console.navbar',
@@ -231,8 +233,7 @@ angular
templateUrl: baseTemplate
});
- $urlRouterProvider.otherwise('/');
-
+ $urlRouterProvider.otherwise('/404');
$locationProvider.html5Mode(true);
}])
.run(['$rootScope', '$state', 'MetaTags', 'gettingStarted', ($root, $state, $meta, gettingStarted) => {
http://git-wip-us.apache.org/repos/asf/ignite/blob/f1cdb871/modules/web-console/frontend/app/modules/navbar/userbar.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/navbar/userbar.directive.js b/modules/web-console/frontend/app/modules/navbar/userbar.directive.js
index b10d5ef..af70eb1 100644
--- a/modules/web-console/frontend/app/modules/navbar/userbar.directive.js
+++ b/modules/web-console/frontend/app/modules/navbar/userbar.directive.js
@@ -18,7 +18,7 @@
export default ['igniteUserbar', [function() {
return {
restrict: 'A',
- controller: ['$rootScope', 'IgniteUserbar', function($root, IgniteUserbar) {
+ controller: ['$rootScope', 'IgniteUserbar', 'AclService', function($root, IgniteUserbar, AclService) {
const ctrl = this;
ctrl.items = [
@@ -26,15 +26,15 @@ export default ['igniteUserbar', [function() {
{text: 'Getting started', click: 'gettingStarted.tryShow(true)'}
];
- const _rebuildSettings = (event, user) => {
+ const _rebuildSettings = () => {
ctrl.items.splice(2);
- if (!user.becomeUsed && user.admin)
+ if (AclService.can('admin_page'))
ctrl.items.push({text: 'Admin panel', sref: 'settings.admin'});
ctrl.items.push(...IgniteUserbar);
- if (!user.becomeUsed)
+ if (AclService.can('logout'))
ctrl.items.push({text: 'Log out', sref: 'logout'});
};
http://git-wip-us.apache.org/repos/asf/ignite/blob/f1cdb871/modules/web-console/frontend/app/modules/states/admin.state.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/admin.state.js b/modules/web-console/frontend/app/modules/states/admin.state.js
index af1fbde..c3151e1 100644
--- a/modules/web-console/frontend/app/modules/states/admin.state.js
+++ b/modules/web-console/frontend/app/modules/states/admin.state.js
@@ -21,12 +21,13 @@ angular
.module('ignite-console.states.admin', [
'ui.router'
])
-.config(['$stateProvider', function($stateProvider) {
+.config(['$stateProvider', 'AclRouteProvider', function($stateProvider, AclRoute) {
// set up the states
$stateProvider
.state('settings.admin', {
url: '/admin',
templateUrl: '/settings/admin.html',
+ onEnter: AclRoute.checkAccess('admin_page'),
metaTags: {
title: 'List of registered users'
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/f1cdb871/modules/web-console/frontend/app/modules/states/configuration.state.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/configuration.state.js b/modules/web-console/frontend/app/modules/states/configuration.state.js
index fd9bd1c..7fd7541 100644
--- a/modules/web-console/frontend/app/modules/states/configuration.state.js
+++ b/modules/web-console/frontend/app/modules/states/configuration.state.js
@@ -32,16 +32,18 @@ angular.module('ignite-console.states.configuration', ['ui.router'])
// Services.
.service('igniteConfigurationResource', ConfigurationResource)
// Configure state provider.
- .config(['$stateProvider', ($stateProvider) => {
+ .config(['$stateProvider', 'AclRouteProvider', ($stateProvider, AclRoute) => {
// Setup the states.
$stateProvider
.state('base.configuration', {
url: '/configuration',
- templateUrl: '/configuration/sidebar.html'
+ templateUrl: '/configuration/sidebar.html',
+ abstract: true
})
.state('base.configuration.clusters', {
url: '/clusters',
templateUrl: '/configuration/clusters.html',
+ onEnter: AclRoute.checkAccess('configuration'),
params: {
linkId: null
},
@@ -52,6 +54,7 @@ angular.module('ignite-console.states.configuration', ['ui.router'])
.state('base.configuration.caches', {
url: '/caches',
templateUrl: '/configuration/caches.html',
+ onEnter: AclRoute.checkAccess('configuration'),
params: {
linkId: null
},
@@ -62,6 +65,7 @@ angular.module('ignite-console.states.configuration', ['ui.router'])
.state('base.configuration.domains', {
url: '/domains',
templateUrl: '/configuration/domains.html',
+ onEnter: AclRoute.checkAccess('configuration'),
params: {
linkId: null
},
@@ -72,6 +76,7 @@ angular.module('ignite-console.states.configuration', ['ui.router'])
.state('base.configuration.igfs', {
url: '/igfs',
templateUrl: '/configuration/igfs.html',
+ onEnter: AclRoute.checkAccess('configuration'),
params: {
linkId: null
},
@@ -82,6 +87,7 @@ angular.module('ignite-console.states.configuration', ['ui.router'])
.state('base.configuration.summary', {
url: '/summary',
templateUrl: '/configuration/summary.html',
+ onEnter: AclRoute.checkAccess('configuration'),
controller: ConfigurationSummaryCtrl,
controllerAs: 'ctrl',
metaTags: {
http://git-wip-us.apache.org/repos/asf/ignite/blob/f1cdb871/modules/web-console/frontend/app/modules/states/errors.state.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/errors.state.js b/modules/web-console/frontend/app/modules/states/errors.state.js
new file mode 100644
index 0000000..df31f57
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/states/errors.state.js
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+import angular from 'angular';
+import templateNotFoundPage from '../../../views/404.jade';
+import templateNotAuthorizedPage from '../../../views/403.jade';
+
+angular
+ .module('ignite-console.states.errors', [
+ 'ui.router'
+ ])
+ .config(['$stateProvider', 'AclRouteProvider', function($stateProvider) {
+ // set up the states
+ $stateProvider
+ .state('404', {
+ url: '/404',
+ templateUrl: templateNotFoundPage
+ })
+ .state('403', {
+ url: '/403',
+ templateUrl: templateNotAuthorizedPage
+ });
+ }]);
http://git-wip-us.apache.org/repos/asf/ignite/blob/f1cdb871/modules/web-console/frontend/app/modules/states/logout.state.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/logout.state.js b/modules/web-console/frontend/app/modules/states/logout.state.js
index 7f24a45..42795ea 100644
--- a/modules/web-console/frontend/app/modules/states/logout.state.js
+++ b/modules/web-console/frontend/app/modules/states/logout.state.js
@@ -21,14 +21,13 @@ angular
.module('ignite-console.states.logout', [
'ui.router'
])
-.config(['$stateProvider', function($stateProvider) {
+.config(['$stateProvider', 'AclRouteProvider', function($stateProvider, AclRoute) {
// set up the states
$stateProvider
.state('logout', {
url: '/logout',
- controller: ['Auth', function(Auth) {
- Auth.logout();
- }],
+ onEnter: AclRoute.checkAccess('logout'),
+ controller: ['Auth', (Auth) => Auth.logout()],
metaTags: {
title: 'Logout'
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/f1cdb871/modules/web-console/frontend/app/modules/states/password.state.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/password.state.js b/modules/web-console/frontend/app/modules/states/password.state.js
index 48d01df..2eb030c 100644
--- a/modules/web-console/frontend/app/modules/states/password.state.js
+++ b/modules/web-console/frontend/app/modules/states/password.state.js
@@ -21,7 +21,7 @@ angular
.module('ignite-console.states.password', [
'ui.router'
])
-.config(['$stateProvider', function($stateProvider) {
+.config(['$stateProvider', 'AclRouteProvider', function($stateProvider, AclRoute) {
// set up the states
$stateProvider
.state('password', {
@@ -32,6 +32,7 @@ angular
.state('password.reset', {
url: '/reset?{token}',
templateUrl: '/reset.html',
+ onEnter: AclRoute.checkAccess('login'),
metaTags: {
title: 'Reset password'
}
@@ -39,6 +40,7 @@ angular
.state('password.send', {
url: '/send',
templateUrl: '/reset.html',
+ onEnter: AclRoute.checkAccess('login'),
metaTags: {
title: 'Password Send'
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/f1cdb871/modules/web-console/frontend/app/modules/states/profile.state.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/profile.state.js b/modules/web-console/frontend/app/modules/states/profile.state.js
index 8b6cdfe..9c31340 100644
--- a/modules/web-console/frontend/app/modules/states/profile.state.js
+++ b/modules/web-console/frontend/app/modules/states/profile.state.js
@@ -21,12 +21,13 @@ angular
.module('ignite-console.states.profile', [
'ui.router'
])
-.config(['$stateProvider', function($stateProvider) {
+.config(['$stateProvider', 'AclRouteProvider', function($stateProvider, AclRoute) {
// set up the states
$stateProvider
.state('settings.profile', {
url: '/profile',
templateUrl: '/settings/profile.html',
+ onEnter: AclRoute.checkAccess('profile'),
metaTags: {
title: 'User profile'
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/f1cdb871/modules/web-console/frontend/app/modules/states/signin.state.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/states/signin.state.js b/modules/web-console/frontend/app/modules/states/signin.state.js
index a23a496..42175cf 100644
--- a/modules/web-console/frontend/app/modules/states/signin.state.js
+++ b/modules/web-console/frontend/app/modules/states/signin.state.js
@@ -24,30 +24,20 @@ angular
// services
'ignite-console.user'
])
-.config(['$stateProvider', function($stateProvider) {
+.config(['$stateProvider', 'AclRouteProvider', function($stateProvider) {
// set up the states
$stateProvider
.state('signin', {
url: '/',
templateUrl,
- metaTags: {
- }
- });
-}])
-.run(['$rootScope', '$state', 'Auth', 'IgniteBranding', function($root, $state, Auth, branding) {
- $root.$on('$stateChangeStart', function(event, toState) {
- if (toState.name === branding.termsState)
- return;
-
- if (!Auth.authorized && (toState.name !== 'signin' && !_.startsWith(toState.name, 'password.'))) {
- event.preventDefault();
+ onEnter: ['$state', 'Auth', 'AclService', ($state, Auth, AclService) => {
+ if (Auth.authorized)
+ $state.go('base.configuration.clusters');
- $state.go('signin');
- }
- else if (Auth.authorized && toState.name === 'signin') {
- event.preventDefault();
-
- $state.go('base.configuration.clusters');
+ if (!AclService.can('login'))
+ $state.go('403');
+ }],
+ metaTags: {
}
});
}]);
http://git-wip-us.apache.org/repos/asf/ignite/blob/f1cdb871/modules/web-console/frontend/app/modules/user/AclRoute.provider.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/user/AclRoute.provider.js b/modules/web-console/frontend/app/modules/user/AclRoute.provider.js
new file mode 100644
index 0000000..cfd9da1
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/user/AclRoute.provider.js
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+export default [() => {
+ class AclRoute {
+ static checkAccess = (permissions, failState) => {
+ failState = failState || '403';
+
+ return ['$state', 'AclService', 'Auth', ($state, AclService) => {
+ if (AclService.can(permissions))
+ return;
+
+ return $state.go(failState);
+ }];
+ }
+ }
+
+ return {
+ checkAccess: AclRoute.checkAccess,
+ $get: () => {
+ return AclRoute;
+ }
+ };
+}];
http://git-wip-us.apache.org/repos/asf/ignite/blob/f1cdb871/modules/web-console/frontend/app/modules/user/permissions.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/user/permissions.js b/modules/web-console/frontend/app/modules/user/permissions.js
new file mode 100644
index 0000000..ef10884
--- /dev/null
+++ b/modules/web-console/frontend/app/modules/user/permissions.js
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+const guest = ['login', 'terms'];
+const becomed = ['profile', 'configuration', 'query', 'terms'];
+const user = becomed.concat(['logout']);
+const admin = user.concat(['admin_page']);
+
+export default {
+ guest,
+ user,
+ admin,
+ becomed
+};
http://git-wip-us.apache.org/repos/asf/ignite/blob/f1cdb871/modules/web-console/frontend/app/modules/user/user.module.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/modules/user/user.module.js b/modules/web-console/frontend/app/modules/user/user.module.js
index 2387f20..a480f97 100644
--- a/modules/web-console/frontend/app/modules/user/user.module.js
+++ b/modules/web-console/frontend/app/modules/user/user.module.js
@@ -16,13 +16,38 @@
*/
import angular from 'angular';
+import aclData from './permissions';
import Auth from './Auth.service';
import User from './User.service';
+import AclRouteProvider from './AclRoute.provider';
angular
.module('ignite-console.user', [
-
+ 'mm.acl',
+ 'ignite-console.config'
])
.service(...Auth)
-.service(...User);
+.service(...User)
+.provider('AclRoute', AclRouteProvider)
+.run(['$rootScope', 'AclService', ($root, AclService) => {
+ AclService.setAbilities(aclData);
+ AclService.attachRole('guest');
+
+ $root.$on('user', (event, user) => {
+ if (!user)
+ return;
+
+ AclService.flushRoles();
+
+ let role = 'user';
+
+ if (user.admin)
+ role = 'admin';
+
+ if (user.becomeUsed)
+ role = 'becomed';
+
+ AclService.attachRole(role);
+ });
+}]);
http://git-wip-us.apache.org/repos/asf/ignite/blob/f1cdb871/modules/web-console/frontend/app/vendor.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/vendor.js b/modules/web-console/frontend/app/vendor.js
index a8eeea7..0322887 100644
--- a/modules/web-console/frontend/app/vendor.js
+++ b/modules/web-console/frontend/app/vendor.js
@@ -17,6 +17,7 @@
import 'jquery';
import 'angular';
+import 'angular-acl';
import 'angular-animate';
import 'angular-sanitize';
import 'angular-strap';
http://git-wip-us.apache.org/repos/asf/ignite/blob/f1cdb871/modules/web-console/frontend/package.json
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/package.json b/modules/web-console/frontend/package.json
index 9bc6987..5814522 100644
--- a/modules/web-console/frontend/package.json
+++ b/modules/web-console/frontend/package.json
@@ -30,6 +30,7 @@
],
"dependencies": {
"angular": "^1.5.5",
+ "angular-acl": "^0.1.7",
"angular-animate": "^1.5.5",
"angular-aria": "^1.5.5",
"angular-cookies": "^1.5.5",
http://git-wip-us.apache.org/repos/asf/ignite/blob/f1cdb871/modules/web-console/frontend/public/stylesheets/blocks/error.scss
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/public/stylesheets/blocks/error.scss b/modules/web-console/frontend/public/stylesheets/blocks/error.scss
new file mode 100644
index 0000000..20f83a8
--- /dev/null
+++ b/modules/web-console/frontend/public/stylesheets/blocks/error.scss
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+.error-page {
+ text-align: center;
+ min-height: 300px;
+
+ &__title {
+ margin-top: 100px;
+ font-weight: 200;
+ }
+
+ &__description {
+ margin-top: 30px;
+ font-weight: 500;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/f1cdb871/modules/web-console/frontend/public/stylesheets/style.scss
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/public/stylesheets/style.scss b/modules/web-console/frontend/public/stylesheets/style.scss
index 994595a..4db7127 100644
--- a/modules/web-console/frontend/public/stylesheets/style.scss
+++ b/modules/web-console/frontend/public/stylesheets/style.scss
@@ -20,6 +20,7 @@
@import "./variables";
@import "~roboto-font/css/fonts.css";
@import "./../../app/directives/information/information.scss";
+@import "./blocks/error";
hr {
margin: 20px 0;
http://git-wip-us.apache.org/repos/asf/ignite/blob/f1cdb871/modules/web-console/frontend/views/403.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/views/403.jade b/modules/web-console/frontend/views/403.jade
new file mode 100644
index 0000000..1a1cc04
--- /dev/null
+++ b/modules/web-console/frontend/views/403.jade
@@ -0,0 +1,30 @@
+//-
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+
+header#header.header
+ table.container
+ tr
+ td.col-xs-3.col-sm-3.col-md-2
+ ignite-header-logo
+ td
+ ignite-header-title
+
+.error-page
+ .container
+ h1.error-page__title 403
+ h2.error-page__description You not authorized
+
+include includes/footer
http://git-wip-us.apache.org/repos/asf/ignite/blob/f1cdb871/modules/web-console/frontend/views/404.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/views/404.jade b/modules/web-console/frontend/views/404.jade
new file mode 100644
index 0000000..3533905
--- /dev/null
+++ b/modules/web-console/frontend/views/404.jade
@@ -0,0 +1,30 @@
+//-
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+
+header#header.header
+ table.container
+ tr
+ td.col-xs-3.col-sm-3.col-md-2
+ ignite-header-logo
+ td
+ ignite-header-title
+
+.error-page
+ .container
+ h1.error-page__title 404
+ h2.error-page__description Page not found
+
+include includes/footer