@ -0,0 +1,131 @@ | |||||
'use strict'; | |||||
angular.module('Authentication') | |||||
.factory('AuthenticationService', | |||||
['Base64', '$http', '$cookieStore', '$rootScope', '$timeout', | |||||
function (Base64, $http, $cookieStore, $rootScope, $timeout) { | |||||
var service = {}; | |||||
service.Login = function (username, password, callback) { | |||||
var authdata = Base64.encode(username + ':' + password); | |||||
$http.get('/api/users/'+username, {headers: {Authorization: 'Basic ' + authdata}}) | |||||
.then(function (response) { | |||||
service.SetCredentials(username, password); | |||||
callback({success: true}); | |||||
}).catch(function (response) { | |||||
service.ClearCredentials(); | |||||
callback({success: false, message: 'Username or password is incorrect'}); | |||||
}); | |||||
}; | |||||
service.SetCredentials = function (username, password) { | |||||
var authdata = Base64.encode(username + ':' + password); | |||||
$rootScope.globals = { | |||||
currentUser: { | |||||
username: username, | |||||
authdata: authdata | |||||
} | |||||
}; | |||||
$http.defaults.headers.common['Authorization'] = 'Basic ' + authdata; // jshint ignore:line | |||||
$cookieStore.put('globals', $rootScope.globals); | |||||
}; | |||||
service.ClearCredentials = function () { | |||||
$rootScope.globals = {}; | |||||
$cookieStore.remove('globals'); | |||||
$http.defaults.headers.common.Authorization = 'Basic '; | |||||
}; | |||||
return service; | |||||
}]) | |||||
.factory('Base64', function () { | |||||
/* jshint ignore:start */ | |||||
var keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; | |||||
return { | |||||
encode: function (input) { | |||||
var output = ""; | |||||
var chr1, chr2, chr3 = ""; | |||||
var enc1, enc2, enc3, enc4 = ""; | |||||
var i = 0; | |||||
do { | |||||
chr1 = input.charCodeAt(i++); | |||||
chr2 = input.charCodeAt(i++); | |||||
chr3 = input.charCodeAt(i++); | |||||
enc1 = chr1 >> 2; | |||||
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); | |||||
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); | |||||
enc4 = chr3 & 63; | |||||
if (isNaN(chr2)) { | |||||
enc3 = enc4 = 64; | |||||
} else if (isNaN(chr3)) { | |||||
enc4 = 64; | |||||
} | |||||
output = output + | |||||
keyStr.charAt(enc1) + | |||||
keyStr.charAt(enc2) + | |||||
keyStr.charAt(enc3) + | |||||
keyStr.charAt(enc4); | |||||
chr1 = chr2 = chr3 = ""; | |||||
enc1 = enc2 = enc3 = enc4 = ""; | |||||
} while (i < input.length); | |||||
return output; | |||||
}, | |||||
decode: function (input) { | |||||
var output = ""; | |||||
var chr1, chr2, chr3 = ""; | |||||
var enc1, enc2, enc3, enc4 = ""; | |||||
var i = 0; | |||||
// remove all characters that are not A-Z, a-z, 0-9, +, /, or = | |||||
var base64test = /[^A-Za-z0-9\+\/\=]/g; | |||||
if (base64test.exec(input)) { | |||||
window.alert("There were invalid base64 characters in the input text.\n" + | |||||
"Valid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\n" + | |||||
"Expect errors in decoding."); | |||||
} | |||||
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); | |||||
do { | |||||
enc1 = keyStr.indexOf(input.charAt(i++)); | |||||
enc2 = keyStr.indexOf(input.charAt(i++)); | |||||
enc3 = keyStr.indexOf(input.charAt(i++)); | |||||
enc4 = keyStr.indexOf(input.charAt(i++)); | |||||
chr1 = (enc1 << 2) | (enc2 >> 4); | |||||
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); | |||||
chr3 = ((enc3 & 3) << 6) | enc4; | |||||
output = output + String.fromCharCode(chr1); | |||||
if (enc3 != 64) { | |||||
output = output + String.fromCharCode(chr2); | |||||
} | |||||
if (enc4 != 64) { | |||||
output = output + String.fromCharCode(chr3); | |||||
} | |||||
chr1 = chr2 = chr3 = ""; | |||||
enc1 = enc2 = enc3 = enc4 = ""; | |||||
} while (i < input.length); | |||||
return output; | |||||
} | |||||
}; | |||||
/* jshint ignore:end */ | |||||
}); |
@ -0,0 +1,23 @@ | |||||
'use strict'; | |||||
angular.module('Authentication') | |||||
.controller('LoginController', | |||||
['$scope', '$rootScope', '$location', 'AuthenticationService', | |||||
function ($scope, $rootScope, $location, AuthenticationService) { | |||||
// reset login status | |||||
AuthenticationService.ClearCredentials(); | |||||
$scope.login = function () { | |||||
$scope.dataLoading = true; | |||||
AuthenticationService.Login($scope.username, $scope.password, function (response) { | |||||
if (response.success) { | |||||
$location.path('/users/'+$scope.username); | |||||
} else { | |||||
console.log(response); | |||||
$scope.error = response.message; | |||||
$scope.dataLoading = false; | |||||
} | |||||
}); | |||||
}; | |||||
}]); |
@ -0,0 +1 @@ | |||||
!function(){"use strict";angular.module("http-auth-interceptor",["http-auth-interceptor-buffer"]).factory("authService",["$rootScope","httpBuffer",function($rootScope,httpBuffer){return{loginConfirmed:function(data,configUpdater){var updater=configUpdater||function(config){return config};$rootScope.$broadcast("event:auth-loginConfirmed",data),httpBuffer.retryAll(updater)},loginCancelled:function(data,reason){httpBuffer.rejectAll(reason),$rootScope.$broadcast("event:auth-loginCancelled",data)}}}]).config(["$httpProvider",function($httpProvider){$httpProvider.interceptors.push(["$rootScope","$q","httpBuffer",function($rootScope,$q,httpBuffer){return{responseError:function(rejection){var config=rejection.config||{};if(!config.ignoreAuthModule)switch(rejection.status){case 401:var deferred=$q.defer(),bufferLength=httpBuffer.append(config,deferred);return 1===bufferLength&&$rootScope.$broadcast("event:auth-loginRequired",rejection),deferred.promise;case 403:$rootScope.$broadcast("event:auth-forbidden",rejection)}return $q.reject(rejection)}}}])}]),angular.module("http-auth-interceptor-buffer",[]).factory("httpBuffer",["$injector",function($injector){function retryHttpRequest(config,deferred){function successCallback(response){deferred.resolve(response)}function errorCallback(response){deferred.reject(response)}$http=$http||$injector.get("$http"),$http(config).then(successCallback,errorCallback)}var $http,buffer=[];return{append:function(config,deferred){return buffer.push({config:config,deferred:deferred})},rejectAll:function(reason){if(reason)for(var i=0;i<buffer.length;++i)buffer[i].deferred.reject(reason);buffer=[]},retryAll:function(updater){for(var i=0;i<buffer.length;++i){var _cfg=updater(buffer[i].config);_cfg!==!1&&retryHttpRequest(_cfg,buffer[i].deferred)}buffer=[]}}}])}(),"undefined"!=typeof module&&"undefined"!=typeof exports&&module.exports===exports&&(module.exports="http-auth-interceptor"); |
@ -0,0 +1,190 @@ | |||||
<!DOCTYPE html> | |||||
<html> | |||||
<head> | |||||
<meta name="viewport" content="width=device-width, initial-scale=1"> | |||||
<title>LILiK Users</title> | |||||
<link rel="stylesheet" href="https://cdn.gitcdn.link/cdn/angular/bower-material/v1.1.3/angular-material.css"> | |||||
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.3/angular.min.js"></script> | |||||
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.3/angular-animate.min.js"></script> | |||||
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.3/angular-aria.min.js"></script> | |||||
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.3/angular-route.min.js"></script> | |||||
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.3/angular-cookies.min.js"></script> | |||||
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.3/angular-messages.min.js"></script> | |||||
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.3/angular-sanitize.min.js"></script> | |||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic"> | |||||
<script src="https://cdn.gitcdn.link/cdn/angular/bower-material/v1.1.3/angular-material.js"></script> | |||||
<script> | |||||
var sericesDescriptions = { | |||||
admin: "You can access LILiK machines as root, edit users and much more...", | |||||
mail: "<a href='https://webmail.lilik.it' target='_blank'>Go to your webmail</a>" | |||||
} | |||||
angular.module('Authentication', []); | |||||
var app = angular.module("app", | |||||
[ | |||||
'Authentication', | |||||
'ngRoute', | |||||
'ngCookies', | |||||
'ngMaterial', | |||||
'ngMessages', | |||||
'ngSanitize' | |||||
]); | |||||
app.config(['$routeProvider', function ($routeProvider) { | |||||
$routeProvider.when('/login', { | |||||
controller: 'LoginController', | |||||
templateUrl: 'views/login.html', | |||||
hideMenus: true | |||||
}).when('/users/:ID', { | |||||
// controller: 'HomeController', | |||||
templateUrl: 'views/show.html' | |||||
}).when('/users/:ID/edit', { | |||||
controller: 'EditController', | |||||
templateUrl: 'views/edit.html' | |||||
}).otherwise({ redirectTo: '/login' }); | |||||
}]).run(['$rootScope', '$location', '$cookieStore', '$http', 'AuthenticationService', '$mdDialog', | |||||
function ($rootScope, $location, $cookieStore, $http, AuthenticationService, $mdDialog) { | |||||
$rootScope.getUsers = function(searchText) { | |||||
return $http.get("/api/users").then(function(response) { | |||||
return response.data.filter(function(user) { | |||||
return user.startsWith(searchText); | |||||
}); | |||||
}); | |||||
}; | |||||
$rootScope.$watch('globals.currentUser', function() { | |||||
if (typeof $rootScope.globals.currentUser !== 'undefined') { | |||||
$http.get("/api/users/"+$rootScope.globals.currentUser.username).then(function(response) { | |||||
$rootScope.logged_user = response.data; | |||||
console.log(response.data); | |||||
}); | |||||
} | |||||
}); | |||||
// keep user logged in after page refresh | |||||
$rootScope.globals = $cookieStore.get('globals') || {}; | |||||
if ($rootScope.globals.currentUser) { | |||||
$http.defaults.headers.common['Authorization'] = 'Basic ' + $rootScope.globals.currentUser.authdata; // jshint ignore:line | |||||
} | |||||
$rootScope.logout = function(){ | |||||
AuthenticationService.ClearCredentials(); | |||||
$location.path('/login'); | |||||
}; | |||||
$rootScope.promptNewUser = function(ev) { | |||||
// Appending dialog to document.body to cover sidenav in docs app | |||||
var confirm = $mdDialog.prompt() | |||||
.title('Create new user') | |||||
.textContent('Choose a uid for the new user') | |||||
.placeholder('username') | |||||
.ariaLabel('username') | |||||
.initialValue('') | |||||
.targetEvent(ev) | |||||
.ok('Create') | |||||
.cancel('Cancel'); | |||||
$mdDialog.show(confirm).then(function(result) { | |||||
$http.post("/api/users", {uid: result}).then(function(response) { | |||||
console.log(response.data); | |||||
$rootScope.editUser(result); | |||||
}); | |||||
}, function(){}); | |||||
}; | |||||
$rootScope.getServiceDescription = function(service){ | |||||
if (service in sericesDescriptions){ | |||||
return sericesDescriptions[service] | |||||
} | |||||
return service | |||||
}; | |||||
$rootScope.editUser = function(user){ | |||||
$location.path('/users/'+user+'/edit'); | |||||
} | |||||
$rootScope.home = function(){ | |||||
$location.path('/users/'+$rootScope.globals.currentUser.username); | |||||
} | |||||
$rootScope.$on('$locationChangeStart', function (event, next, current) { | |||||
// redirect to login page if not logged in | |||||
if ($location.path() !== '/login' && !$rootScope.globals.currentUser) { | |||||
$location.path('/login'); | |||||
} | |||||
}); | |||||
}]).controller('EditController', ['$scope', '$routeParams', '$http', function($scope, $routeParams, $http) { | |||||
console.log($routeParams.ID); | |||||
if ($routeParams.ID == $scope.globals.currentUser.username){ | |||||
$scope.user = $scope.logged_user; | |||||
}else{ | |||||
$http.get("/api/users/"+$routeParams.ID).then(function(response) { | |||||
$scope.user = response.data; | |||||
console.log(response.data); | |||||
}); | |||||
} | |||||
$scope.save = function(){ | |||||
$http.put("/api/users/"+$routeParams.ID, $scope.user).then(function(response) { | |||||
console.log(response.data); | |||||
}); | |||||
}; | |||||
}]);; | |||||
// controller("myCtrl", function($scope, $http, authService) { | |||||
// $scope.$on('event:auth-loginRequired', function() { | |||||
// console.log(54546); | |||||
// }); | |||||
// | |||||
// $scope.$on('event:auth-loginRequired', function() { | |||||
// console.log("catch"); | |||||
// }); | |||||
// | |||||
// | |||||
// | |||||
// $http.get("/api/users/test").then(function(response) { | |||||
// $scope.user = response.data; | |||||
// console.log(response.data); | |||||
// }); | |||||
// // $http.get("/api/users").then(function(response) { | |||||
// // $scope.users = response.data; | |||||
// // }); | |||||
// }); | |||||
</script> | |||||
<script src="authentication.js"></script> | |||||
<script src="controllers.js"></script> | |||||
<style> | |||||
#search { | |||||
display: inline-flex; | |||||
} | |||||
#search md-input-container { | |||||
margin: 0; | |||||
padding-bottom: -10px; | |||||
} | |||||
#search .md-errors-spacer { | |||||
display: none; | |||||
} | |||||
#search md-progress-linear.md-inline { | |||||
bottom: 1px; | |||||
right: 2px; | |||||
left: 2px; | |||||
width: auto; | |||||
height: 1px; | |||||
} | |||||
</style> | |||||
</head> | |||||
<body ng-app="app" ng-cloak> | |||||
<div layout="row" layout-padding layout-align="space-between center" ng-if="globals.currentUser"> | |||||
<div> | |||||
<md-button ng-click="home();">Home</md-button> | |||||
<md-button ng-click="promptNewUser()" ng-if="logged_user.services.admin">New</md-button> | |||||
<md-autocomplete ng-if="logged_user.services.admin" id="search" md-selected-item="selectedItem" | |||||
md-search-text="searchText" md-select-on-match=true md-match-case-insensitive=false | |||||
md-require-match=true md-floating-label ="Search user" md-selected-item-change="editUser(item)" md-items="item in getUsers(searchText)" md-item-text="item" flex="25"> | |||||
<span md-highlight-text="searchText">{{item}}</span> | |||||
</md-autocomplete> | |||||
</div> | |||||
<div class="nav-buttons"> | |||||
<!-- <md-button class="md-raised md-accent">My profile</md-button> --> | |||||
<md-button class="md-raised md-warn" ng-click="logout()">Log Out</md-button> | |||||
</div> | |||||
</div> | |||||
<div ng-view></div> | |||||
</body> | |||||
</html> |
@ -0,0 +1,20 @@ | |||||
<div ng-if="user"> | |||||
<md-toolbar> | |||||
<div class="md-toolbar-tools"> | |||||
<h2><span>Edit {{user.uid}}</span></h2> | |||||
</div> | |||||
</md-toolbar> | |||||
<md-content layout-padding> | |||||
<md-input-container class="md-block"> | |||||
<label>Common name</label> | |||||
<input ng-model="user.cn" required autofocus> | |||||
</md-input-container> | |||||
<h3>Services</h3> | |||||
<div ng-repeat="(service, status) in user.services"> | |||||
<md-checkbox aria-label="{{service}}" ng-model="user.services[service]">{{service}}</md-checkbox> | |||||
</div> | |||||
<md-button type="submit" class="md-raised md-primary" ng-click="save()">Save</md-button> | |||||
</md-content> | |||||
</div> | |||||
<!-- <div ng-repeat="(service, status) in user.services" ng-if="status">{{service}} --> |
@ -0,0 +1,25 @@ | |||||
<div layout="row" layout-align="center none"> | |||||
<md-card flex="flex" flex-gt-sm="50" flex-gt-md="33"> | |||||
<md-toolbar> | |||||
<div class="md-toolbar-tools"> | |||||
<h2><span>Login Form</span></h2> | |||||
</div> | |||||
</md-toolbar> | |||||
<md-card-content> | |||||
<form name="Form" ng-submit="login()"> | |||||
<md-input-container class="md-block"> | |||||
<div ng-messages="error">{{error}}</div> | |||||
</md-input-container> | |||||
<md-input-container class="md-block"> | |||||
<label>Username</label> | |||||
<input ng-model="username" required autofocus> | |||||
</md-input-container> | |||||
<md-input-container class="md-block"> | |||||
<label>Password</label> | |||||
<input ng-model="password" type="password" required> | |||||
</md-input-container> | |||||
<md-button ng-disabled="!Form.$valid" type="submit" class="md-raised md-primary">Login</md-button> | |||||
</form> | |||||
</md-card-content> | |||||
</md-card> | |||||
</div> |
@ -0,0 +1,10 @@ | |||||
<div ng-if="logged_user"> | |||||
<md-toolbar> | |||||
<div class="md-toolbar-tools"> | |||||
<h2><span>Hi {{logged_user.uid}} <span class="md-subhead">aka</span> {{logged_user.cn}}</span></h2> | |||||
</div> | |||||
</md-toolbar> | |||||
<md-content layout-padding> | |||||
<div ng-repeat="(service, status) in logged_user.services" ng-if="status" ng-bind-html="getServiceDescription(service)"></div> | |||||
</md-content> | |||||
</div> |