Django Rest Framework include_docs_urls adding _0 to action - django

We have urls in our Django (Rest Framework) applications like:
r'^endpoint/(?P<item>[a-z_-]+)/$'
r'^endpoint/(?P<item>[a-z_-]+)/(?P<version>[0-9]+(\.[0-9])?)/$'
Both have POST methods available.
We've been using Swagger for a while to document our API but wanted to look at the coreapi documentation included in Django Rest Framework.
Going through our documentation based on the above structure the coreapi action results in:
# Initialize a client & load the schema document
client = coreapi.Client()
schema = client.get("http://localhost:8081/docs/")
# Interact with the first url
action = ["app", "endpoint > create"]
# Interact with the second url
action = ["app", "endpoint > create_0"]
I can understand where create_0 is coming from, but ideally it would add the keyword name as a suffix instead, e.g. create_version.
Is this possible?

Having two keywords right after each other seems to be the problem.
r'^endpoint/(?P<item>[a-z_-]+)/$'
r'^endpoint/(?P<item>[a-z_-]+)/(?P<version>[0-9]+(\.[0-9])?)/$'
Should be replaced with:
r'^endpoint/(?P<item>[a-z_-]+)/$'
r'^endpoint/(?P<item>[a-z_-]+)/version/(?P<version>[0-9]+(\.[0-9])?)/$'
That will give you:
action = ["endpoint", "item > version > create"]
Which looks much cleaner.

Related

RESTful API: is it good to have foreign key which sends to different version of the API?

We are developing a RESTful API using Django REST framework, and we decided to handle foreign keys showing a URL to the relation's resource, for example:
GET https://url_to_api/api/v2/foo/1:
{
"id": 1,
"bar": "https://url_to_api/api/v2/bar/6/",
"baz": "https://url_to_api/api/v3/baz/4/"
}
This is a GET request for the foo's ID 1 on the version 2 of the API.
Django REST framework returns the URL to the v2 for the relation with the bar entity because /api/v2/bar/6/ sends to a view which is only used by this version, but it returns an URL to v3 (https://url_to_api/api/v3/baz/4/) for the relation with the baz entity, because its view is the same as the v2's one, and Django REST framework reverse engine returns the first result for the URL which sends to the same view.
I have a couple of questions about this behaviour:
is it a normal behaviour or we are missing or doing something wrong?
is it good to have URLs which sends to a different version of the API?
Any other ideas on how to manage this?
This is a normal behaviour in DRF. So either you can specify like this or else you specify custom for everything.
# bookings/urls.py
urlpatterns = [
url(r'^$', bookings_list, name='bookings-list'),
url(r'^(?P<pk>[0-9]+)/$', bookings_detail, name='bookings-detail')
]
# urls.py
urlpatterns = [
url(r'^v1/bookings/', include('bookings.urls', namespace='v1')),
url(r'^v2/bookings/', include('bookings.urls', namespace='v2'))
]
For further reference, refer this

Integrate Swagger 0.3.x with Django and Rest Framework

Working on rather old project running Django 1.6 there's a need to integrate Swagger to describe REST endpoints.
I've installed compatible version of Django Rest Framework (3.2.5) and Django Rest Swagger (0.3.0), then imported both into INSTALLED APPS and included DRF-Swagger's urls in my url scheme:
...
url(r'^api/v1/$', include('rest_framework_swagger.urls')),
...
When I go to this URL I see that Swagger is working, but I can't understand what should I do next to make it work with my endpoints and show information about them? Should I add something to SWAGGER_SETTINGS to be able to read YAML insertions from methods?
The fastest way to configure this version of swagger to work with DRF is first to install both packages compatible with Django 1.6:
pip install djangorestframework==3.2.5
pip install django-rest-swagger==0.3.0
Then you should add both to installed apps in project settings:
INSTALLED_APPS = (
...
'rest_framework',
'rest_framework_swagger',
)
Optionally you can add SWAGGER_SETTINGS to project settings, but it's not mandatory, here's a link to SWAGGER_SETTINGS.
Then you should add this pattern to your urls:
url(r'^docs/', include('rest_framework_swagger.urls'))
you can make it extend existing path, for example:
url(r'^<your root path>/docs/', include('rest_framework_swagger.urls'))
but the key is that this url should end with exactly docs/ to be able to access swagger. At this point if everything is done correctly you should be able to access Swagger at:
<your root path>/docs/
Last thing you need to do is import and decorate your endpoint View with api_view decorator:
from rest_framework.decorators import api_view
...
#api_view(["GET"])
def my_api_view(request, parameter_a, parameter_b):
"""
Endpoint returns list of open orders
---
parameters:
- name: parameter_a
description: Description for parameter a
required: true
paramType: path
- name: parameter_b
description: Description for parameter b
required: true
paramType: path
"""
...rest of View...
This will tell Swagger that this endpoint is related to DRF and has description to be displayed at <your root path>/docs/

Dropbox and Django SSO using SAML

Summary
I am looking to use Dropbox SSO functionality by using the authentication from a Django site. Note that I'm not looking to use SAML as a backend for my Django site.
Resources
1) Dropbox Custom SSO help page: https://www.dropbox.com/en/help/1921#custom
2) Creating a SAML response: https://robinelvin.wordpress.com/2009/09/04/saml-with-django/
3) Struggled to find any examples from Google of people doing this kind of SSO. Lots of links about people using SAML as a Django backend.
Question
In the dropbox admin settings I can add my X509 certificate and the login link. This means that when you try to login into Dropbox using SSO it nicely forwards you to my Django site's login page using a GET request with a SAMLRequest in the querystring.
However, my understanding is that I now need to, once the user is authenticated on the Django site, fire a POST request back to Dropbox at their SAML login link with a SAMLResponse in the post data. Using the second resource above I believe I can create the SAMLResponse xml but I am unsure how to redirect the user to the dropbox SAML login link with the SAML data from my Django view.
Any help much appreciated.
Managed to get the functionality I needed using django-saml2-idp https://github.com/peopledoc/django-saml2-idp
Good documentation on installing here: https://github.com/peopledoc/django-saml2-idp/blob/master/doc/INSTALL.txt
Settings in the Dropbox Admin console required the X509 certificate and then the login url set to: https://****.com/idp/login
Note that I had issues installing the M2Crypto dependency so used an Ubuntu package via:
sudo apt-get install python-m2crypto
Additionally I'm using Django 1.9.6 so needed to make overrides to the views.py, urls.py, and registry.py files to make them compatible (various import statements needed updating and the urls changed to the new list format rather than using patterns).
Created a Dropbox Processor as follows:
import base64
import zlib
from saml2idp import base
from saml2idp.xml_render import _get_assertion_xml
def get_assertion_dropbox_xml(parameters, signed=False):
return _get_assertion_xml(ASSERTION_DROPBOX, parameters, signed)
ASSERTION_DROPBOX = (
'<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" '
'ID="${ASSERTION_ID}" '
'IssueInstant="${ISSUE_INSTANT}" '
'Version="2.0">'
'<saml:Issuer>${ISSUER}</saml:Issuer>'
'${ASSERTION_SIGNATURE}'
'${SUBJECT_STATEMENT}'
'<saml:Conditions NotBefore="${NOT_BEFORE}" NotOnOrAfter="${NOT_ON_OR_AFTER}">'
'<saml:AudienceRestriction>'
'<saml:Audience>${AUDIENCE}</saml:Audience>'
'</saml:AudienceRestriction>'
'</saml:Conditions>'
'<saml:AuthnStatement AuthnInstant="${AUTH_INSTANT}"'
'>'
'<saml:AuthnContext>'
'<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef>'
'</saml:AuthnContext>'
'</saml:AuthnStatement>'
'${ATTRIBUTE_STATEMENT}'
'</saml:Assertion>'
)
class Processor(base.Processor):
def _decode_request(self):
"""
Decodes _request_xml from _saml_request.
"""
self._request_xml = zlib.decompress(base64.b64decode(self._saml_request), -15)
def _format_assertion(self):
self._assertion_xml = get_assertion_dropbox_xml(self._assertion_params, signed=False)
Which you register in your settings.py file as follows:
SAML2IDP_CONFIG = {
'autosubmit': True,
'certificate_file': '/****/certificate.pem',
'private_key_file': '/****/private-key.pem',
'issuer': 'https://www.****.com',
'signing': True,
}
sampleSpConfig = {
'acs_url': 'https://www.dropbox.com/saml_login',
'processor': 'dropbox.Processor',
}
SAML2IDP_REMOTES = {
'sample': sampleSpConfig,
}
Works like a dream. Hope this helps somebody out there.

django socialauth twitter , google oauth , facebook does not work

This is my first post, and I have a problem I could not make it work django OMAB socialauth of three things I just need to google, facebook, and twitter, google works well with open id, but not much twitter and I put in my
settings. py:
TWITTER_CONSUMER_KEY = '00' this is no real
TWITTER_CONSUMER_SECRET = '00' this is no real
FACEBOOK_APP_ID = '' ihave no key
FACEBOOK_API_SECRET = ''
LINKEDIN_CONSUMER_KEY = ''
LINKEDIN_CONSUMER_SECRET = ''
ORKUT_CONSUMER_KEY = ''
ORKUT_CONSUMER_SECRET = ''ihave no key
GOOGLE_OAUTH2_CLIENT_ID = ''
GOOGLE_OAUTH2_CLIENT_SECRET = ''
SOCIAL_AUTH_CREATE_USERS = True
SOCIAL_AUTH_FORCE_RANDOM_USERNAME = False
SOCIAL_AUTH_DEFAULT_USERNAME = 'socialauth_user'
SOCIAL_AUTH_COMPLETE_URL_NAME = 'socialauth_complete'
LOGIN_ERROR_URL = '/login/error/'
#SOCIAL_AUTH_USER_MODEL = 'app.CustomUser'
SOCIAL_AUTH_ERROR_KEY = 'socialauth_error'
GITHUB_APP_ID = ''
GITHUB_API_SECRET = ''
FOURSQUARE_CONSUMER_KEY = ''
FOURSQUARE_CONSUMER_SECRET = ''
LOGIN_URL = '/login-form/'
LOGIN_REDIRECT_URL = '/'
LOGIN_ERROR_URL = '/login-error/'
I am using the example that comes in the zip of OMAB socialauth django , but not working.
When I created my twitter app, I wrote my domain www.sisvei.com , I am testing locally socialauth django ie 127.0.0.1:8000, then sign in with twitter sends me to this url:
http://127.0.0.1:8000/login/error/ and a message saying is the Incorrect authentication service
this happens with facebook and google oauth and oauth2
I'm new to django and I this much work comprising this part of django socialath hopefully help me, thank you very much.
You need to be more specific on "why it doesn't work". Where are you getting the errors?
When debugging a third-party oauth/openid app in Django, generally it boils down to:
configuration & keys - did you make sure to obtain all of the necessary API keys for the services you will be using, and to add them to your configuration?
urls - did you remember to add the necessary urlpatterns to your base urls.py file?
authentication setup on the server - often, you'll need to have a file available or respond with a specific header when the authentication service hits your server. Have you checked to make sure that is set up?
databases - have you run syncdb after installing the app? Are all the tables set up?
templates - if the third party app requires you to set up templates, do you have them set up?
custom views - are you using custom views? If so, try using the built-in views that came with the third party app first, to see if they work
After those are confirmed, you're going to want to be able to see what requests are taking place. Use the debugger included in Chrome/Safari, or get the web developer add-on for Firefox, and look at the network requests as they happen. Do you see HTTP responses other than 200 (say, 404, 500, 403, etc?) those mean that the services aren't responding correctly.
From your error, it looks like you have not correctly set up your callback URL on Twitter. It should be sending you to www.sisvei.com, not 127.0.0.1. Alternatively, check the URL when you get to the Twitter login page -- is the callback URL in the URL, and is it pointing to 127.0.0.1? Then Django is sending it the wrong callback URL.
Finally this:
I wrote my domain www.sisvei.com python does not support this
Is unclear. As far as I know, Python doesn't care what the domain is.
WAIT A MINUTE ...
Are you using runserver? Are you getting the following error?
Error: "www.sisvei.com" is not a valid port number or address:port pair.
If so, there is an easy fix! Just run it like so:
python manage.py runserver www.sisvei.com:80
That should resolve your error if that's what's happening. You're probably running it as
python manage.py runserver 127.0.0.1
127.0.0.1 is a reserved IP address that points back to localhost, your own computer. As a result, it is not possible to use it for authentication or any other purpose outside of programs running on your own machine. See this article for more info.
I'm not sure, but I might be having similar problems, oscar. For me, SocialAuth was generating an AuthenticationURL for facebook, foursquare and hotmail, but not for google, twitter or any of the other address it supports. I think it may be something wrong with the API, so I posted an issue on the social-auth google group...you may want to check there to see if anyone updates!!
https://code.google.com/p/socialauth/issues/detail?id=282&colspec=ID%20Type%20Status%20Priority%20Milestone%20Owner%20Summary%20Modified

CF8 and Salesforce REST API - updating records

I'm trying to do integration with Salesforce using their REST API and CF8.
I got the OAuth bit working, getting data etc but now I'm trying to update some records in Contact table.
First I tought about doing it the "proper" way as their docs say -
Update a record using HTTP PATCH.
But CFHTTP doesn't support PATCH method.
So then I tried running a SOQL query:
UPDATE Contact SET MailingStreet = 'Blah Blah' WHERE Id = '003A000000Zp4ObIAJ'
but here I'm getting
{"message":"unexpected token: UPDATE","errorCode":"MALFORMED_QUERY"}
Does anyone have an idea how to do it?
You can create your own PATCH method if your client supports it, but there is an easier way. From the Force.com REST API Developer's Guide:
If you use an HTTP library that doesn't allow overriding or setting an
arbitrary HTTP method name, you can send a POST request and provide an
override to the HTTP method via the query string parameter
_HttpMethod. In the PATCH example, you can replace the PostMethod line
with one that doesn't use override:
PostMethod m = new PostMethod(url + "?_HttpMethod=PATCH");
In CF9 CFScript, using the method that Paddyslacker already suggested for adding _HttpMethod=PATCH to the URL:
private boolean function patchObject(required string sfid, required string type, required struct obj) {
local.url = variables.salesforceInstance & '/services/data/v' & variables.apiVersion &'/sobjects/' & arguments.type & '/' & arguments.sfid &'?_HttpMethod=PATCH';
local.http = new Http(url=local.url,method='post');
//... convert obj to a json string, add to local.http ...
local.httpSendResult = local.http.send().getPrefix();
}
We have a CF9 CFC that we wrote that wraps most of the REST API that we will be open sourcing soon. I'll come back and link to it when we do.