Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gn oauth2 int #3

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
0b4333a
- GeoNode A&A OAuth2 Integration
Oct 14, 2016
9656554
#2696 Part 1 - GNIP - Better management of View and Download permissi…
Nov 4, 2016
934e703
- fix issues
Nov 4, 2016
6eee9e8
Merge branch 'master' of https://github.com/GeoNode/geonode into GN_O…
Nov 4, 2016
707edd8
- Merge with master
Nov 4, 2016
e9b5195
- Merge with master
Nov 4, 2016
bfd2465
- #2696 Part 2 - GNIP - Better management of View and Download permi…
Nov 4, 2016
31af720
Merge branch 'master' of https://github.com/GeoNode/geonode into GN_O…
Nov 4, 2016
00fc78d
Merge branch 'master' of https://github.com/GeoNode/geonode into GN_O…
Nov 7, 2016
1aa95e9
Process inbox messages using AJAX
travislbrundage Nov 7, 2016
3fb165f
- removed import on INSTALLED_SCHEMES since its usage was removed on …
Nov 8, 2016
b4f0d70
Merge pull request #2708 from BerryDaniel/versions_fix
simod Nov 8, 2016
bf488b2
Merge pull request #2707 from travislbrundage/ajax-messages
simod Nov 8, 2016
5dd5cec
Split geoserver.helpers.set_attributes() (#2699)
JivanAmara Nov 8, 2016
a0e1eb8
require celery version to be at least 3.1.18
Nov 8, 2016
0e2836b
Merge pull request #2710 from harts-boundless/celery_min_version_3.1.18
sarasafavi Nov 8, 2016
49be1ba
The standalone TEMPLATE_* settings were deprecated in Django 1.8 (#2711)
Nov 10, 2016
8b835bd
- merge with master
Nov 11, 2016
5237247
- Avoid errors if OAuth2 App is not defined
Nov 11, 2016
30254b6
- revert pavement change
Nov 11, 2016
6acce4c
- GeoServer / GeoNode Advanced A&A Documentation
Nov 11, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dev_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ MIGRATE_APPS:
- people
- base
- services
- oauth2_provider
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
928 changes: 928 additions & 0 deletions docs/tutorials/admin/geoserver_geonode_security/index.txt

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions docs/tutorials/admin/index.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ You will know how to:
.. toctree::
:hidden:

geoserver_geonode_security/index
admin_panel/index
admin_mgmt_commands/index
csw_settings/index
Expand All @@ -40,6 +41,9 @@ You will know how to:
s3_config/index
backup_restore/index

:ref:`geoserver_geonode_aa`
GeoNode interacts with GeoServer through an advanced security mechanism based on OAuth2 Protocol and GeoFence. This section is a walk through of the configuration and setup of GeoNode and GeoServer Advanced Security.

:ref:`admin_panel`
GeoNode has an administration panel based on the Django admin which can be used to do some database operations. Although most of the operations can and should be done through the normal GeoNode interface, the admin panel provides a quick overview and management tool over the database.

Expand Down
195 changes: 195 additions & 0 deletions geonode/api/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
# -*- coding: utf-8 -*-
#########################################################################
#
# Copyright (C) 2016 OSGeo
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#########################################################################

import json

from django.utils import timezone
from oauth2_provider.models import AccessToken
from oauth2_provider.exceptions import OAuthToolkitError, FatalClientError
from django.views.decorators.csrf import csrf_exempt
from django.conf import settings
from django.contrib.auth import models, authenticate, login, get_user_model
from django.http import HttpResponse, HttpResponseRedirect

from guardian.models import Group

def verify_access_token(key):
try:
token = AccessToken.objects.get(token=key)

if not token.is_valid():
raise OAuthToolkitError('AccessToken is not valid.')
if token.is_expired():
raise OAuthToolkitError('AccessToken has expired.')
except AccessToken.DoesNotExist, e:
raise FatalClientError("AccessToken not found at all.")

return token


def get_client_ip(request):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
else:
ip = request.META.get('REMOTE_ADDR')
return ip


@csrf_exempt
def verify_token(request):
"""
TODO: Check IP whitelist / blacklist
Verifies the velidity of an OAuth2 Access Token
and returns associated User's details
"""
if (not request.user.is_authenticated()):
return HttpResponse(
json.dumps({
'error': 'unauthorized_request'
}),
status = 403,
content_type="application/json"
)

if (request.POST and request.POST['token']):
try:
token = verify_access_token(request.POST['token'])
except Exception, e:
return HttpResponse(
json.dumps({
'error': str(e)
}),
status = 403,
content_type="application/json"
)

return HttpResponse(
json.dumps({
'client_id': token.application.client_id,
'issued_to': token.user.username,
'user_id': token.user.id,
'email': token.user.email,
'verified_email': 'true',
'access_type': 'online',
'expires_in': (token.expires - timezone.now()).total_seconds() * 1000
}),
content_type="application/json"
)

return HttpResponse(
json.dumps({
'error': 'invalid_request'
}),
status = 403,
content_type="application/json"
)


@csrf_exempt
def roles(request):
"""
Check IP whitelist / blacklist
"""
if settings.AUTH_IP_WHITELIST and not get_client_ip(request) in settings.AUTH_IP_WHITELIST:
return HttpResponse(
json.dumps({
'error': 'unauthorized_request'
}),
status = 403,
content_type="application/json"
)

groups = [group.name for group in Group.objects.all()]
groups.append("admin")

return HttpResponse(
json.dumps({
'groups': groups
}),
content_type="application/json"
)


@csrf_exempt
def users(request):
"""
Check IP whitelist / blacklist
"""
if settings.AUTH_IP_WHITELIST and not get_client_ip(request) in settings.AUTH_IP_WHITELIST:
return HttpResponse(
json.dumps({
'error': 'unauthorized_request'
}),
status = 403,
content_type="application/json"
)

user_name = request.path_info.rsplit('/', 1)[-1]
User = get_user_model()

if user_name is None or not user_name or user_name == "users":
users = [user for user in User.objects.all()]
else:
users = [user for user in User.objects.filter(username=user_name)]

if not users:
# Try using the user email
users = [user for user in User.objects.filter(email=user_name)]

json_object = []
for user in users:
groups = [group.name for group in user.groups.all()]
if user.is_superuser:
groups.append("admin")

json_object.append({
'username': user.username,
'groups': groups
})

return HttpResponse(
json.dumps({
'users': json_object
}),
content_type="application/json"
)


@csrf_exempt
def admin_role(request):
"""
Check IP whitelist / blacklist
"""
if settings.AUTH_IP_WHITELIST and not get_client_ip(request) in settings.AUTH_IP_WHITELIST:
return HttpResponse(
json.dumps({
'error': 'unauthorized_request'
}),
status = 403,
content_type="application/json"
)

return HttpResponse(
json.dumps({
'adminRole': 'admin'
}),
content_type="application/json"
)
113 changes: 113 additions & 0 deletions geonode/base/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
import math
import os
import logging
import uuid
import urllib
import urllib2
import cookielib

from pyproj import transform, Proj
from urlparse import urljoin, urlsplit
Expand All @@ -36,6 +40,7 @@
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth import get_user_model
from django.db.models import signals
from django.contrib.auth.signals import user_logged_in, user_logged_out
from django.core.files.storage import default_storage as storage
from django.core.files.base import ContentFile

Expand All @@ -57,6 +62,10 @@

from geonode.people.enumerations import ROLE_VALUES

from oauthlib.common import generate_token
from oauth2_provider.models import AccessToken, get_application_model
from oauth2_provider.exceptions import OAuthToolkitError, FatalClientError

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -866,3 +875,107 @@ def rating_post_save(instance, *args, **kwargs):
ResourceBase.objects.filter(id=instance.object_id).update(rating=instance.rating)

signals.post_save.connect(rating_post_save, sender=OverallRating)


def do_login(sender, user, request, **kwargs):
"""
Take action on user login. Generate a new user access_token to be shared
with GeoServer, and store it into the request.session
"""
if user and user.is_authenticated():
token = None
try:
Application = get_application_model()
app = Application.objects.get(name="GeoServer")

# Lets create a new one
token = generate_token()

AccessToken.objects.get_or_create(user=user,
application=app,
expires=datetime.datetime.now() + datetime.timedelta(days=1),
token=token)
except:
u = uuid.uuid1()
token = u.hex

# Do GeoServer Login
url = "%s%s?access_token=%s" % (settings.OGC_SERVER['default']['PUBLIC_LOCATION'], 'ows?service=wms&version=1.3.0&request=GetCapabilities', token)

cj = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))

jsessionid = None
try:
home = opener.open(url)
for c in cj:
if c.name == "JSESSIONID":
jsessionid = c.value
except:
u = uuid.uuid1()
jsessionid = u.hex

request.session['access_token'] = token
request.session['JSESSIONID'] = jsessionid


def do_logout(sender, user, request, **kwargs):
"""
Take action on user logout. Cleanup user access_token and send logout
request to GeoServer
"""
if 'access_token' in request.session:
Application = get_application_model()
app = Application.objects.get(name="GeoServer")

# Lets delete the old one
try:
old = AccessToken.objects.get(user=user, application=app)
except:
pass
else:
old.delete()

# Do GeoServer Logout
url = "%s%s?access_token=%s" % (settings.OGC_SERVER['default']['PUBLIC_LOCATION'],
settings.OGC_SERVER['default']['LOGOUT_ENDPOINT'],
request.session['access_token'])

header_params = {
"Authorization": ("Bearer %s" % request.session['access_token'])
}

param = {}
data = urllib.urlencode(param)

cookies = None
for cook in request.COOKIES:
name = str(cook)
value = request.COOKIES.get(name)
if name == 'csrftoken':
header_params['X-CSRFToken'] = value

cook = "%s=%s" % (name,value)
if not cookies:
cookies = cook
else:
cookies = cookies + '; ' + cook

if cookies:
if 'JSESSIONID' in request.session and request.session['JSESSIONID']:
cookies = cookies + '; JSESSIONID=' + request.session['JSESSIONID']
header_params['Cookie'] = cookies

gs_request = urllib2.Request(url, data, header_params)

try:
response = urllib2.urlopen(gs_request).open()
except:
pass

del request.session['access_token']
request.session.modified = True


user_logged_in.connect(do_login)
user_logged_out.connect(do_logout)
22 changes: 12 additions & 10 deletions geonode/contrib/geosites/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from django.db import models
from django.db.models import signals
from django.contrib.sites.models import Site
from django.conf import settings

from geonode.base.models import ResourceBase
from geonode.layers.models import Layer
Expand Down Expand Up @@ -98,13 +99,14 @@ def post_delete_profile(instance, sender, **kwargs):


# Django doesn't propagate the signals to the parents so we need to add the listeners on the children
signals.post_save.connect(post_save_resource, sender=Layer)
signals.post_save.connect(post_save_resource, sender=Map)
signals.post_save.connect(post_save_resource, sender=Document)
signals.post_save.connect(post_save_site, sender=Site)
signals.post_delete.connect(post_delete_resource, sender=Layer)
signals.post_delete.connect(post_delete_resource, sender=Map)
signals.post_delete.connect(post_delete_resource, sender=Document)
signals.post_delete.connect(post_delete_site, sender=Site)
signals.post_save.connect(post_save_profile, sender=Profile)
signals.post_delete.connect(post_delete_profile, sender=Profile)
if 'geonode.contrib.geosites' in settings.INSTALLED_APPS:
signals.post_save.connect(post_save_resource, sender=Layer)
signals.post_save.connect(post_save_resource, sender=Map)
signals.post_save.connect(post_save_resource, sender=Document)
signals.post_save.connect(post_save_site, sender=Site)
signals.post_delete.connect(post_delete_resource, sender=Layer)
signals.post_delete.connect(post_delete_resource, sender=Map)
signals.post_delete.connect(post_delete_resource, sender=Document)
signals.post_delete.connect(post_delete_site, sender=Site)
signals.post_save.connect(post_save_profile, sender=Profile)
signals.post_delete.connect(post_delete_profile, sender=Profile)
Loading