@ -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> |