commit
38b48604f3
|
@ -262,11 +262,18 @@ for those plugins.
|
||||||
|
|
||||||
Authentication
|
Authentication
|
||||||
--------------
|
--------------
|
||||||
Lemur currently supports Basic Authentication and Ping OAuth2 out of the box. Additional flows can be added relatively easily.
|
Lemur currently supports Basic Authentication, Ping OAuth2, and Google out of the box. Additional flows can be added relatively easily.
|
||||||
If you are not using Ping you do not need to configure any of these options.
|
If you are not using an authentication provider you do not need to configure any of these options.
|
||||||
|
|
||||||
For more information about how to use social logins, see: `Satellizer <https://github.com/sahat/satellizer>`_
|
For more information about how to use social logins, see: `Satellizer <https://github.com/sahat/satellizer>`_
|
||||||
|
|
||||||
|
.. data:: ACTIVE_PROVIDERS
|
||||||
|
:noindex:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
ACTIVE_PROVIDERS = ["ping", "google"]
|
||||||
|
|
||||||
.. data:: PING_SECRET
|
.. data:: PING_SECRET
|
||||||
:noindex:
|
:noindex:
|
||||||
|
|
||||||
|
@ -296,6 +303,33 @@ For more information about how to use social logins, see: `Satellizer <https://g
|
||||||
|
|
||||||
PING_JWKS_URL = "https://<yourpingserver>/pf/JWKS"
|
PING_JWKS_URL = "https://<yourpingserver>/pf/JWKS"
|
||||||
|
|
||||||
|
.. data:: PING_NAME
|
||||||
|
:noindex:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
PING_NAME = "Example Oauth2 Provider"
|
||||||
|
|
||||||
|
.. data:: PING_CLIENT_ID
|
||||||
|
:noindex:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
PING_CLIENT_ID = "client-id"
|
||||||
|
|
||||||
|
.. data:: GOOGLE_CLIENT_ID
|
||||||
|
:noindex:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
GOOGLE_CLIENT_ID = "client-id"
|
||||||
|
|
||||||
|
.. data:: GOOGLE_SECRET
|
||||||
|
:noindex:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
GOOGLE_SECRET = "somethingsecret"
|
||||||
|
|
||||||
|
|
||||||
AWS Plugin Configuration
|
AWS Plugin Configuration
|
||||||
|
|
|
@ -230,5 +230,80 @@ class Ping(Resource):
|
||||||
return dict(token=create_token(user))
|
return dict(token=create_token(user))
|
||||||
|
|
||||||
|
|
||||||
|
class Google(Resource):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.reqparse = reqparse.RequestParser()
|
||||||
|
super(Google, self).__init__()
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
access_token_url = 'https://accounts.google.com/o/oauth2/token'
|
||||||
|
people_api_url = 'https://www.googleapis.com/plus/v1/people/me/openIdConnect'
|
||||||
|
|
||||||
|
self.reqparse.add_argument('clientId', type=str, required=True, location='json')
|
||||||
|
self.reqparse.add_argument('redirectUri', type=str, required=True, location='json')
|
||||||
|
self.reqparse.add_argument('code', type=str, required=True, location='json')
|
||||||
|
|
||||||
|
args = self.reqparse.parse_args()
|
||||||
|
|
||||||
|
# Step 1. Exchange authorization code for access token
|
||||||
|
payload = {
|
||||||
|
'client_id': args['clientId'],
|
||||||
|
'grant_type': 'authorization_code',
|
||||||
|
'redirect_uri': args['redirectUri'],
|
||||||
|
'code': args['code'],
|
||||||
|
'client_secret': current_app.config.get('GOOGLE_SECRET')
|
||||||
|
}
|
||||||
|
|
||||||
|
r = requests.post(access_token_url, data=payload)
|
||||||
|
token = r.json()
|
||||||
|
|
||||||
|
# Step 2. Retrieve information about the current user
|
||||||
|
headers = {'Authorization': 'Bearer {0}'.format(token['access_token'])}
|
||||||
|
|
||||||
|
r = requests.get(people_api_url, headers=headers)
|
||||||
|
profile = r.json()
|
||||||
|
|
||||||
|
user = user_service.get_by_email(profile['email'])
|
||||||
|
|
||||||
|
if user:
|
||||||
|
return dict(token=create_token(user))
|
||||||
|
|
||||||
|
|
||||||
|
class Providers(Resource):
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
|
||||||
|
active_providers = dict()
|
||||||
|
|
||||||
|
for provider in current_app.config.get("ACTIVE_PROVIDERS"):
|
||||||
|
provider = provider.lower()
|
||||||
|
|
||||||
|
if provider == "google":
|
||||||
|
|
||||||
|
active_providers["google"] = {
|
||||||
|
'clientId': current_app.config.get("GOOGLE_CLIENT_ID"),
|
||||||
|
'url': api.url_for(Google)
|
||||||
|
}
|
||||||
|
|
||||||
|
elif provider == "ping":
|
||||||
|
|
||||||
|
active_providers["oauth2"] = {
|
||||||
|
'name': current_app.config.get("PING_NAME"),
|
||||||
|
'url': api.url_for(Ping),
|
||||||
|
'redirectUri': '', # TODO
|
||||||
|
'clientId': current_app.config.get("PING_CLIENT_ID"),
|
||||||
|
'responseType': 'code',
|
||||||
|
'scope': ['openid', 'email', 'profile', 'address'],
|
||||||
|
'scopeDelimeter': ' ',
|
||||||
|
'authorizationEndpoint': '', # TODO
|
||||||
|
'requiredUrlParams': ['scope']
|
||||||
|
}
|
||||||
|
|
||||||
|
return active_providers
|
||||||
|
|
||||||
|
|
||||||
api.add_resource(Login, '/auth/login', endpoint='login')
|
api.add_resource(Login, '/auth/login', endpoint='login')
|
||||||
api.add_resource(Ping, '/auth/ping', endpoint='ping')
|
api.add_resource(Ping, '/auth/ping', endpoint='ping')
|
||||||
|
api.add_resource(Google, '/auth/google', endpoint='google')
|
||||||
|
api.add_resource(Providers, '/auth/providers', endpoint='providers')
|
||||||
|
|
|
@ -18,7 +18,7 @@ var lemur = angular
|
||||||
'angular-clipboard',
|
'angular-clipboard',
|
||||||
'ngFileSaver'
|
'ngFileSaver'
|
||||||
])
|
])
|
||||||
.config(function ($stateProvider, $urlRouterProvider, $authProvider) {
|
.config(function ($stateProvider, $urlRouterProvider, $authProvider, AuthenticationService) {
|
||||||
$urlRouterProvider.otherwise('/welcome');
|
$urlRouterProvider.otherwise('/welcome');
|
||||||
|
|
||||||
$stateProvider
|
$stateProvider
|
||||||
|
@ -27,17 +27,18 @@ var lemur = angular
|
||||||
templateUrl: 'angular/welcome/welcome.html'
|
templateUrl: 'angular/welcome/welcome.html'
|
||||||
});
|
});
|
||||||
|
|
||||||
$authProvider.oauth2({
|
AuthenticationService.get_providers().then(function (active_providers) {
|
||||||
name: 'example',
|
var provider_names = [];
|
||||||
url: 'http://localhost:8000/api/1/auth/ping',
|
for (var key in active_providers) {
|
||||||
redirectUri: 'http://localhost:3000/',
|
if (active_providers.hasOwnProperty(key)) {
|
||||||
clientId: 'client-id',
|
provider_names.push(key);
|
||||||
responseType: 'code',
|
}
|
||||||
scope: ['openid', 'email', 'profile', 'address'],
|
}
|
||||||
scopeDelimiter: ' ',
|
|
||||||
authorizationEndpoint: 'https://example.com/as/authorization.oauth2',
|
for (var i=0; i < provider_names.length; i++) {
|
||||||
requiredUrlParams: ['scope']
|
$authProvider[provider_names[i]](active_providers[provider_names[i]]);
|
||||||
});
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
lemur.service('MomentService', function () {
|
lemur.service('MomentService', function () {
|
||||||
|
|
|
@ -12,6 +12,7 @@ angular.module('lemur')
|
||||||
$scope.login = AuthenticationService.login;
|
$scope.login = AuthenticationService.login;
|
||||||
$scope.authenticate = AuthenticationService.authenticate;
|
$scope.authenticate = AuthenticationService.authenticate;
|
||||||
$scope.logout = AuthenticationService.logout;
|
$scope.logout = AuthenticationService.logout;
|
||||||
|
$scope.get_providers = AuthenticationService.get_providers;
|
||||||
|
|
||||||
UserService.getCurrentUser().then(function (user) {
|
UserService.getCurrentUser().then(function (user) {
|
||||||
$scope.currentUser = user;
|
$scope.currentUser = user;
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
<div class="login">
|
<div class="login">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12 col-sm-12 col-md-12">
|
<div class="col-xs-12 col-sm-12 col-md-12">
|
||||||
<button class="btn btn-block btn-default" ng-click="authenticate('Example')">
|
<button class="btn btn-block btn-default" ng-repeat="(key, value) in get_providers()" ng-click="authenticate(key)">
|
||||||
Login with Example
|
Login with {{key}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,6 +6,10 @@ angular.module('lemur')
|
||||||
.service('AuthenticationService', function ($location, $rootScope, AuthenticationApi, UserService, toaster, $auth) {
|
.service('AuthenticationService', function ($location, $rootScope, AuthenticationApi, UserService, toaster, $auth) {
|
||||||
var AuthenticationService = this;
|
var AuthenticationService = this;
|
||||||
|
|
||||||
|
AuthenticationService.get_providers = function () {
|
||||||
|
return AuthenticationApi.one('providers').get();
|
||||||
|
};
|
||||||
|
|
||||||
AuthenticationService.login = function (username, password) {
|
AuthenticationService.login = function (username, password) {
|
||||||
AuthenticationApi.customPOST({'username': username, 'password': password}, 'login')
|
AuthenticationApi.customPOST({'username': username, 'password': password}, 'login')
|
||||||
.then(
|
.then(
|
||||||
|
|
|
@ -30,8 +30,7 @@
|
||||||
Password
|
Password
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input type="password" name="password" ng-model="user.password" placeholder="hunter2" class="form-control" required/>
|
<input type="password" name="password" ng-model="user.password" placeholder="hunter2" class="form-control" />
|
||||||
<p ng-show="createForm.password.$invalid && !createForm.password.$pristine" class="help-block">You must enter an password</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
|
@ -52,7 +52,10 @@ class User(db.Model):
|
||||||
:param password:
|
:param password:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
return bcrypt.check_password_hash(self.password, password)
|
if self.password:
|
||||||
|
return bcrypt.check_password_hash(self.password, password)
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
def hash_password(self):
|
def hash_password(self):
|
||||||
"""
|
"""
|
||||||
|
@ -60,8 +63,11 @@ class User(db.Model):
|
||||||
|
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
self.password = bcrypt.generate_password_hash(self.password)
|
if self.password:
|
||||||
return self.password
|
self.password = bcrypt.generate_password_hash(self.password)
|
||||||
|
return self.password
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_admin(self):
|
def is_admin(self):
|
||||||
|
|
|
@ -157,7 +157,7 @@ class UsersList(AuthenticatedResource):
|
||||||
"""
|
"""
|
||||||
self.reqparse.add_argument('username', type=str, location='json', required=True)
|
self.reqparse.add_argument('username', type=str, location='json', required=True)
|
||||||
self.reqparse.add_argument('email', type=str, location='json', required=True)
|
self.reqparse.add_argument('email', type=str, location='json', required=True)
|
||||||
self.reqparse.add_argument('password', type=str, location='json', required=True)
|
self.reqparse.add_argument('password', type=str, location='json', default=None)
|
||||||
self.reqparse.add_argument('active', type=bool, default=True, location='json')
|
self.reqparse.add_argument('active', type=bool, default=True, location='json')
|
||||||
self.reqparse.add_argument('roles', type=roles, default=[], location='json')
|
self.reqparse.add_argument('roles', type=roles, default=[], location='json')
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue