diff --git a/gulpfile.js b/gulpfile.js index 88d1f0c51..835993e76 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -120,6 +120,15 @@ gulp.task('js-terminal', function () { .pipe(gulp.dest('dist/js')); }); +gulp.task('js-login', function () { + return gulp.src(['src/js/login.js']) + .pipe(ejs({ oauth: oauth, appstore: appstore }, {}, { ext: '.js' })) + .pipe(sourcemaps.init()) + .pipe(concat('login.js', { newLine: ';' })) + .pipe(sourcemaps.write()) + .pipe(gulp.dest('dist/js')); +}); + gulp.task('js-setup', function () { return gulp.src(['src/js/setup.js', 'src/js/client.js']) .pipe(ejs({ oauth: oauth, appstore: appstore }, {}, { ext: '.js' })) @@ -147,7 +156,7 @@ gulp.task('js-restore', function () { .pipe(gulp.dest('dist/js')); }); -gulp.task('js', gulp.series([ 'js-index', 'js-logs', 'js-terminal', 'js-setup', 'js-setupdns', 'js-restore' ])); +gulp.task('js', gulp.series([ 'js-index', 'js-logs', 'js-terminal', 'js-login', 'js-setup', 'js-setupdns', 'js-restore' ])); // -------------- // HTML @@ -162,7 +171,7 @@ gulp.task('html-templates', function () { }); gulp.task('html-raw', function () { - return gulp.src('src/*.html').pipe(ejs({ revision: revision }, {}, { ext: '.html' })).pipe(gulp.dest('dist')); + return gulp.src('src/*.html').pipe(ejs({ apiOrigin: oauth.apiOrigin, revision: revision }, {}, { ext: '.html' })).pipe(gulp.dest('dist')); }); gulp.task('html', gulp.series(['html-views', 'html-templates', 'html-raw'])); @@ -214,6 +223,7 @@ gulp.task('watch', function (done) { gulp.watch(['src/js/restore.js', 'src/js/client.js'], gulp.series(['js-restore'])); gulp.watch(['src/js/logs.js', 'src/js/client.js'], gulp.series(['js-logs'])); gulp.watch(['src/js/terminal.js', 'src/js/client.js'], gulp.series(['js-terminal'])); + gulp.watch(['src/js/login.js'], gulp.series(['js-login'])); gulp.watch(['src/js/index.js', 'src/js/client.js', 'src/js/main.js', 'src/views/*.js'], gulp.series(['js-index'])); gulp.watch(['src/3rdparty/**/*'], gulp.series(['3rdparty'])); done(); diff --git a/src/js/client.js b/src/js/client.js index 352b11aeb..10bb67dc3 100644 --- a/src/js/client.js +++ b/src/js/client.js @@ -1751,28 +1751,16 @@ angular.module('Application').service('Client', ['$http', '$interval', '$timeout Client.prototype.login = function () { this.setToken(null); - this._userInfo = {}; - var callbackURL = window.location.protocol + '//' + window.location.host + '/login_callback.html'; - var scope = 'root,profile,apps'; - - // generate a state id to protect agains csrf - var state = Math.floor((1 + Math.random()) * 0x1000000000000).toString(16).substring(1); - window.localStorage.oauth2State = state; - - // stash for further use in login_callback - window.localStorage.returnTo = '/' + window.location.hash; - - window.location.href = this.apiOrigin + '/api/v1/oauth/dialog/authorize?response_type=token&client_id=' + this._clientId + '&redirect_uri=' + callbackURL + '&scope=' + scope + '&state=' + state; + window.location.href = '/login.html?returnTo=/' + encodeURIComponent(window.location.hash); }; - Client.prototype.logout = function (allSessions) { + Client.prototype.logout = function () { + var token = this.getToken(); this.setToken(null); - this._userInfo = {}; - // logout from OAuth session - var origin = window.location.protocol + '//' + window.location.host; - window.location.href = this.apiOrigin + '/api/v1/session/logout?redirect=' + origin + (allSessions ? '&all=true' : ''); + // invalidates the token + window.location.href = client.apiOrigin + '/api/v1/cloudron/logout?access_token=' + token; }; // this is ununsed because webadmin uses implicit grant flow diff --git a/src/js/login.js b/src/js/login.js new file mode 100644 index 000000000..315514867 --- /dev/null +++ b/src/js/login.js @@ -0,0 +1,46 @@ +'use strict'; + +/* global angular, $ */ + +// create main application module +var app = angular.module('Application', []); + +app.controller('LoginController', ['$scope', '$http', function ($scope, $http) { + // Stupid angular location provider either wants html5 location mode or not, do the query parsing on my own + var search = decodeURIComponent(window.location.search).slice(1).split('&').map(function (item) { return item.indexOf('=') === -1 ? [item, true] : [item.slice(0, item.indexOf('=')), item.slice(item.indexOf('=')+1)]; }).reduce(function (o, k) { o[k[0]] = k[1]; return o; }, {}); + + $scope.initialized = false; + $scope.busy = false; + $scope.error = null; + $scope.username = ''; + $scope.password = ''; + $scope.totpToken = ''; + + $scope.onLogin = function () { + $scope.busy = true; + $scope.error = null; + + var data = { + username: $scope.username, + password: $scope.password, + totpToken: $scope.totpToken + }; + var apiOrigin = '<%= oauth.apiOrigin %>' || window.location.origin; + + function error() { + $scope.busy = false; + $scope.error = true; + + $scope.password = ''; + $scope.loginForm.$setPristine(); + setTimeout(function () { $('#inputPassword').focus(); }, 200); + } + + $http.post(apiOrigin + '/api/v1/cloudron/login', data).success(function (data, status) { + if (status !== 200) return error(); + + localStorage.token = data.accessToken; + window.location.href = search.returnTo || '/'; + }).error(error); + }; +}]); diff --git a/src/login.html b/src/login.html new file mode 100644 index 000000000..adaeb50b5 --- /dev/null +++ b/src/login.html @@ -0,0 +1,98 @@ + + + + + + + + Cloudron Login + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+
+
+
+ +
+
+
+
+
+

Invalid Credentials

+
+
+
+
+
+
+ + +
+
+ + +
+
+ + +
+ +
+ Reset password +
+
+
+
+ + + +
+ + + + diff --git a/src/setupdns.html b/src/setupdns.html index 11bd50932..b671881f2 100644 --- a/src/setupdns.html +++ b/src/setupdns.html @@ -236,7 +236,7 @@
- +