From 980bf7fda7224cc356cbb9a950cc90f68814a84e Mon Sep 17 00:00:00 2001 From: Simon Yousoufov Date: Thu, 23 Oct 2014 00:21:43 -0400 Subject: [PATCH 01/13] Merged simon_dev --- MontyPyBlog/__init__.pyc | Bin 133 -> 133 bytes MontyPyBlog/models.pyc | Bin 2406 -> 2406 bytes cmsAPI/settings.pyc | Bin 2994 -> 2994 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/MontyPyBlog/__init__.pyc b/MontyPyBlog/__init__.pyc index 3959ed06005a64f0fd7a26f8859baba2fc4082c2..58c0755babc4a4e03eead4793a0bb2a6b0c58184 100644 GIT binary patch delta 14 VcmZo=Y-MC){>;ne+BA`^9snE71N{I1 delta 14 VcmZo=Y-MC){>;lIkT;R79snA21GE4D diff --git a/MontyPyBlog/models.pyc b/MontyPyBlog/models.pyc index 6baf0a31c530772d686947e55835d220bde930b9..315f36ec9f55df0cf4e78ec73cb07b8e929552d6 100644 GIT binary patch delta 15 WcmaDR^h}72`7 Date: Sat, 25 Oct 2014 18:09:23 -0400 Subject: [PATCH 02/13] Create README.md --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..1a33921 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +MontyPyBlog +=========== + +A CMS API written in Python with Django + +Sample Post data response: +{ + "AuthorId": "544bf200438b4c5ac432fdf1", + "Content": "Post content", + "Created On": "2014-10-25 19:01:41.792000", + "Featured Image": "slog-one", + "Gallery Images": "slugs-one", + "Id": "544bf395438b4c5c3943c907", + "Post Type": "post", + "Title": "Post Title #1" +} + +Sample user data response: +{ + "Admin": true, + "Created On": "2014-10-25 19:05:05.511000", + "Email": "user1@user.com", + "Id": "544bf461438b4c5c3943c909", + "Last Login": "2014-10-25 20:05:03", + "Username": "User1" +} From 46b710320f6ecf70de5fdca73627e3180cba4f26 Mon Sep 17 00:00:00 2001 From: Simon Yousoufov Date: Sun, 2 Nov 2014 17:09:50 -0500 Subject: [PATCH 03/13] Added sample app info --- cmsAPI/appinfo_sample.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 cmsAPI/appinfo_sample.py diff --git a/cmsAPI/appinfo_sample.py b/cmsAPI/appinfo_sample.py new file mode 100644 index 0000000..52b42c2 --- /dev/null +++ b/cmsAPI/appinfo_sample.py @@ -0,0 +1,15 @@ +import os, sys + +os.environ['APP_ENVIRONMENT'] = 'local' + +os.environ['AWS_ACCESS_KEY_ID'] = '' +os.environ['AWS_SECRET_ACCESS_KEY'] = '' +os.environ['S3_BUCKET_NAME'] = '' +os.environ['S3_BUCKET_LOCATION'] = '' + +# Unused for now +os.environ['DATABASE_HOST'] = '' +os.environ['DATABASE_USER'] = '' +os.environ['DATABASE_PASS'] = '' + +#os.environ[''] = '' \ No newline at end of file From abdf517da7d4930e1c00dcb99ffcb18ecf1b7d93 Mon Sep 17 00:00:00 2001 From: Simon Yousoufov Date: Sun, 2 Nov 2014 17:12:56 -0500 Subject: [PATCH 04/13] Removed .pyc files --- MontyPyBlog/__init__.pyc | Bin 133 -> 0 bytes MontyPyBlog/admin.pyc | Bin 908 -> 0 bytes MontyPyBlog/serializers.pyc | Bin 1530 -> 0 bytes MontyPyBlog/urls.pyc | Bin 990 -> 0 bytes MontyPyBlog/views.pyc | Bin 4868 -> 0 bytes cmsAPI/__init__.pyc | Bin 128 -> 0 bytes cmsAPI/urls.pyc | Bin 461 -> 0 bytes cmsAPI/wsgi.pyc | Bin 1022 -> 0 bytes 8 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 MontyPyBlog/__init__.pyc delete mode 100644 MontyPyBlog/admin.pyc delete mode 100644 MontyPyBlog/serializers.pyc delete mode 100644 MontyPyBlog/urls.pyc delete mode 100644 MontyPyBlog/views.pyc delete mode 100644 cmsAPI/__init__.pyc delete mode 100644 cmsAPI/urls.pyc delete mode 100644 cmsAPI/wsgi.pyc diff --git a/MontyPyBlog/__init__.pyc b/MontyPyBlog/__init__.pyc deleted file mode 100644 index 58c0755babc4a4e03eead4793a0bb2a6b0c58184..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 133 zcmZSn%**B47V?h9*-#EP^ngT6AT%ydrLF=WO0~~tlVII95juzyi2^2wP)!uv)BCDmmCm8 z@nisNRspCGSWgo}!H^>}iKtc!(VA~ksp_^wnhd%o2i{Fd;{hq*$-3_dJ0fXR9ckAO z^niz3tmrtPDs@{m=%}VL28VHCY$}IhMXalArO~;vovWq374dFnSj4f6}y(_0BDHAXU`+{M% z>s!pMZu>5Ki?>M-VVtYV*)O~wAwdlY9Ef{to&}&NR?t4u6zeu@*EomwS_oPq~=tbl%8}y9%xqNk6Lzog)J_ES7H(lm8*Y X)p|Ag`C9g1<>ViE?hT$leZr$(w_C!S diff --git a/MontyPyBlog/serializers.pyc b/MontyPyBlog/serializers.pyc deleted file mode 100644 index 60e940eb0a86f5c7b529ff2e35f48a929a2a657c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1530 zcmb_b!EVz)5FOizQ$h;`AtbI`@TnGeglL73I0Qu-dLTlwtazQeUE7g&0}5C8H2#PW zfO+H8ajKqBqIkSJo}GE~=56rjWc>2?<>v)$e*wSW@ngTT$@uS)Au2tZd*os7lkuqx zXdX~Gp!tBk0cnq}hm?Di4d@rqPogjLkg{R#L)d*7Q8uDkbi@UUzx<0lUb%-Ts+%G; z#Sh)s$cJ;lW?EY(i%b6O9&J6dE_4t21+u(iqX~$$PuBt9FwPb{_j3zyNNN_LU&%hv zF6~kQo1}WyPsJ@~M@Yrz9^f1>u5Mh&uXOFM>c$P>E}YSB#H&SJIbAu2;1#!#xXnsS z3%O3+y3tuul{e>e*gByI0SGvi*AE+OKEt z)RV-ZAxWyV)JYQcwHDC;VIufN5M z(wR6dKvd-f415vOdc&+UsW4$}2#U(>n;V z`cE9t2e4R}&^z{Je30TH6)1o^^`XK0)faBn2WF&G2YS|w2161SbeR@L+=Wf7OY>YE zbBv77q^vQ<}iZ*|4;Z4^|yj( z6W=2Tg}_5A;VI{Y;5sFIl1nLiXfzX>H#~#yb#v2767fBOpUFM3a&Hs}av*4e%d=CG bg4`5N9)vWJ!JGEVzQu(V@=m?y{)_QH35G!$ diff --git a/MontyPyBlog/urls.pyc b/MontyPyBlog/urls.pyc deleted file mode 100644 index cfbeccdffe9b2098473d406686370550d4f454d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 990 zcmZva+fLg+5Qb-MLrx^59NS*BH@Pq%WnTc%QdLNmS`?*+i>PX3d6&e<_J-^l$Tg4L z2WV#oHi=3dXT9^y&gr+q-|gnj&5v&>JY8LUzQ@0<3jl?;fE=I@FcMG%FbY5r4ImGp zh+q_f&;nElIsg@cj^KI&6yBTg5D0=bhMNrQ9yS=(7&bj@F|0G(^03XY!LZ}uHp3>v zu7@uewixbsxXW;h;j+*k!#2ZxfAWA~hv6Z?hZc;sLA`{p@hem$`-4{;;Lg=?nI+EY zd1+mUMm5i4)C=4OF1MM!vko_4>GZFji||n$98;t$OU(IrpDD-mz^j~DTwvUA?tLLl2-_f$1nEA8B z?B~YV&&|l5eM7A<$;~7yW&Cb%O6E9IXFu;wj@C+lrUUlvdVs0i|00?+EjEi0c;Ygq z@6pksu9I?N`l%_e`Y5*@B7-4|-oo7%Q@X`q@c~;IlIlZx6D$oXJEn||QS ziKoU_q|!2w=2;3jW^0vZWl|uE;HVd>MkWrxHn!`(mKfczTI?~Ja%u`KZC034e$=-z nE$sQ=lU$of(!l-Mg4;SRjH+^dMx)!;xEzRX7>Y31fA;$e2dd>p diff --git a/MontyPyBlog/views.pyc b/MontyPyBlog/views.pyc deleted file mode 100644 index 29fd54f32f8ca5530b43b0e978422a4b7f7948bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4868 zcmcIo%W@mn5$zcOBmq9ahe%O!9&i%Na4eIwEi;L&a)lz9NEMl)2e53TVxu6;kQxZU zz%v)Jm{5yE*<_Vnw)uusD!-8r$RewJK&rCIE;*+!1_Q9Nh+>)KH1F-XxR37Br~4xJ z&&AT;ety5+meG%k|3Ai)yyuAc_;aKy(sQKYh{L`sT~~TJY2>7rmqy-db2_RZje_mx zr8^_NS!vALenGl((kn`%X!|qLElF=)8uQXykj8@5&PsPtdP~w+vi&*fmZi5Wjb-$U z@*9!HiUhObm*muuXO8&uRHD&XRsBM$UrhCDs$WX=MsO&;PnN{lu>N`lx9 zo*fOov2(*J=AdW|!sg>J_%hQ>&$Tp~s}{szs~i3zhz(VoHB;FS&p@A;mKi3tKaYMp ziBForlb|;+ZSI&`E8yA3liYzYA{|FME+IkFyHa_Ie>w5<(#grG$hjj2pOxjQE9b6s z^76SWUtHtZ%TIG+3UZ!fr&i^(M+p~wqa6n=6Zp-jALh8wI>g;DF_q|~GJs){cPlp^ z#Fg#$4fk}t%AI>^P}(SZ&2C40wrwm*pa=Ir?F zRyR7`rfZU|!5Pj8$M^l<3D1&nN^v%v*IdH`{`9ck=Dwz^`xzAHu&zeiBUj>2rH`?(CTION+6fOLZrRB>>+Z6%>=ayG_4r<`-cA8bGw*Zo zzaXYe{P$`5!upD0z!}D+tsMzx^dnZEryfY(g)3lijN9dW_GLj|7A93Qs+yTp&8liv zc42vcPIl=|a9L43iWPII*MQQ$XuF7=hyxqao%5tUxTLd)d7RovyOe6nsdhAKnWw0! z_vzRp@0*b7eRX_m2*<|?Ruh_Tpx$c(&q3c9!b3f2&IW<9$O*!97$Z=_UJG8J?N3|X zZV;bYP2z2GdOE4P!WO~r5$Osmk8G&!IoPZ3*mZ0>u9d-cNuJysCMQJfeeUEkl>*=gR|J7{``AAWRLtDBdy z7wmRe8w|?1 z8Vuj#AjUz)w%%oSa}*jX3@aN2L|;7~2HrKQUZo)IW+C7ajoxb%-=|=fsuCm0#ojhs zS0VHe#4!#*;JpwyYw~QF_CsakpJ3XAo3L!W>RbUI7u;3w@+7u5a+_mpE24RBo^u~h z@+XKXME^lh5oCx^L?aVUfJCX<9})2cZpqT1Z(!d?&5@$smL$&rm)~l60^kxZz!8!x zXn00YMsx@~z{P4ysTMG^+R-RNZ3GvZ+;Xc{A+&VAg{(?%1-(|-RrR2iBwwOJ$^2I( z&MZr2z)=4^m&a*C>v84Z7e!M%2FiX(h*#@Y($j{eCx`T;WeiWrM_u?kG^SkmOF4Uc zI?ble2-6Z#JqicdjH*S=Ra?}|Xi@XIlGCjG+L68^pTL;#F6&5TB1;V&GJ@35fxpeX zWrC4=$e0s_k9Pz8|LTP4RAu8`2WKIw-q9$0hqEwI*|1aHxCEl@Py)TUXyZbxd^qv1 z(4KG#Bdp}CAl^!D*l+%F;o0Qo+ne#8RJZGf0QXk&X|hNtUemEf0i1? zs&PWFxrMy0wJO*jt|hO-yGSHj_{4wJHoVI~o!8C+vjW(>vt&ckYRjp1#P%qa?5LG2 zP{~=A*S)vUoba%EbC)`ep&hnp^Xe*F7q~ma$pv7%$-&PRyJOhc4SfylOvL;i`${Sh zV|GTZTvBFcKr1I_0nOu)QxrQ)M@BCtV9^c6nBH2C5+;^Uqv5M@P{GybH0+;N!hWTd zR6@VULif+_3&yi{q%K`L%yy|DjB%#(0#ag>Jsw_TGQFYFqZJ>E3G zwr#WW)n@H?&kX|(l_-wvQFhPa!!T5k0sKjb@SThI(}a&=KlkQ{$YLq diff --git a/cmsAPI/__init__.pyc b/cmsAPI/__init__.pyc deleted file mode 100644 index 1cc2faf140ad3a4de6319f749388bd999eb4c2d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 128 zcmZSn%**xL#yupN0SXv_v;z*=k+-x||-OFL`{G<^a!-~C$6)28A?NbBB#37Ohn(2j_k5V}g!HnDUfWRlooVBUgkYl?2seqxq!9VPM@k_M z5Uwc9KD{$pu+BHxgV|*?@!j(A|KKW%?S6QTYv*-Wn;V`iaPvzw7c;4ZnqJ6X?g(KV diff --git a/cmsAPI/wsgi.pyc b/cmsAPI/wsgi.pyc deleted file mode 100644 index bedc114fbc2cb7c4596c5413fa181c4b28c9c701..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1022 zcmZWoQE$^Q5O%tCpa231#Phu%`T*%4m=J}Is%Uf*?S!=ld>4Kc2)tPbZ6 ztTFdV94|`gpfaLS7~(va+JP?-+Du;8O6J^4qoHvqV7Y_wn(M;6b|7$r%G4EV4-V}H zZOpX*u6F@nG~0^#BYulDB8gvcNJOU0$9~KaLjD ziy7rR^j_+MdUWL)uG9{8rh%a$6dbqHFSfysh*l^($|jHx(F-bSUmYagUY+QCQFy320h+dlU*w z;1j8jx4fzq5eg^CTM4B0tyZ1j#7+o>TW%3L>S1O~wUj!@w8uMaVMj>+*H*J4F$@in zjaQa5+!}jBZIr+0n)#PBoa4`<+2kUfpDh;C*<_x6xfoxapSh>B)lF!!@4g#E Date: Sun, 2 Nov 2014 17:14:30 -0500 Subject: [PATCH 05/13] Changed TODO notes --- MontyPyBlog/views/post.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/MontyPyBlog/views/post.py b/MontyPyBlog/views/post.py index 3aa6a1d..5484dad 100644 --- a/MontyPyBlog/views/post.py +++ b/MontyPyBlog/views/post.py @@ -17,9 +17,6 @@ """ @TODO: -- Uploading files -- Sending uploaded files to an S3 bucket -- Getting the uploaded file slug(s) - Site info in db (base url, base media url, etc) """ From c936157f116bd3fca971124173c1079a8255ceaa Mon Sep 17 00:00:00 2001 From: Simon Yousoufov Date: Sat, 8 Nov 2014 13:09:36 -0500 Subject: [PATCH 06/13] Implemented django CORS --- cmsAPI/settings.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cmsAPI/settings.py b/cmsAPI/settings.py index 76bcd70..fb642d3 100644 --- a/cmsAPI/settings.py +++ b/cmsAPI/settings.py @@ -98,6 +98,7 @@ ) MIDDLEWARE_CLASSES = ( + 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', @@ -118,6 +119,16 @@ # Don't forget to use absolute paths, not relative paths. ) +# +# CORS +# +CORS_ORIGIN_ALLOW_ALL = False +CORS_ORIGIN_WHITELIST = ( + 'google.com', + 'hostname.example.com', + ) + + INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', @@ -129,6 +140,7 @@ 'djangotoolbox', 'django_mongodb_engine', 'rest_framework', + 'corsheaders', # Uncomment the next line to enable the admin: 'django.contrib.admin', # Uncomment the next line to enable admin documentation: From b24e10cfd7fc26c1869dd66affe1494150f98134 Mon Sep 17 00:00:00 2001 From: Simon Yousoufov Date: Sat, 8 Nov 2014 13:44:00 -0500 Subject: [PATCH 07/13] Added OAuth2 authentication --- MontyPyBlog/urls.py | 2 +- cmsAPI/settings.py | 12 +++++++++++- cmsAPI/urls.py | 2 ++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/MontyPyBlog/urls.py b/MontyPyBlog/urls.py index 9d23a56..d5ea5a9 100644 --- a/MontyPyBlog/urls.py +++ b/MontyPyBlog/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls import patterns, url +from django.conf.urls import patterns, url, include from MontyPyBlog import views diff --git a/cmsAPI/settings.py b/cmsAPI/settings.py index fb642d3..64aab6b 100644 --- a/cmsAPI/settings.py +++ b/cmsAPI/settings.py @@ -126,7 +126,7 @@ CORS_ORIGIN_WHITELIST = ( 'google.com', 'hostname.example.com', - ) +) INSTALLED_APPS = ( @@ -141,12 +141,22 @@ 'django_mongodb_engine', 'rest_framework', 'corsheaders', + 'provider', + 'provider.oauth2', # Uncomment the next line to enable the admin: 'django.contrib.admin', # Uncomment the next line to enable admin documentation: # 'django.contrib.admindocs', ) +REST_FRAMEWORK = { + 'DEFAULT_AUTHENTICATION_CLASSES': ( + # 'rest_framework.authentication.BasicAuthentication', + # 'rest_framework.authentication.SessionAuthentication', + 'rest_framework.authentication.OAuth2Authentication', + ) +} + SESSION_SERIALIZER = 'django.contrib.sessions.serializers.JSONSerializer' # A sample logging configuration. The only tangible logging diff --git a/cmsAPI/urls.py b/cmsAPI/urls.py index dfeba4e..c94e616 100644 --- a/cmsAPI/urls.py +++ b/cmsAPI/urls.py @@ -17,4 +17,6 @@ url(r'^admin/', include(admin.site.urls)), # Points urlconf to the api.urls, which will then handle our urls url(r'^cms/', include('MontyPyBlog.urls')), + # OAuth2 + url(r'^oauth2/', include('provider.oauth2.urls', namespace='oauth2')), ) From 7ca9bc2495ce1a005b9aa05555cae26537356f1d Mon Sep 17 00:00:00 2001 From: Simon Yousoufov Date: Sat, 8 Nov 2014 15:52:04 -0500 Subject: [PATCH 08/13] Oauth authentication checks for current routes --- MontyPyBlog/admin.py | 15 ++++++++------- MontyPyBlog/models.py | 12 +----------- MontyPyBlog/urls.py | 2 +- MontyPyBlog/views/post.py | 20 +++++++++++++++----- MontyPyBlog/views/user.py | 9 +++++++++ 5 files changed, 34 insertions(+), 24 deletions(-) diff --git a/MontyPyBlog/admin.py b/MontyPyBlog/admin.py index ac3d8ad..97ac753 100644 --- a/MontyPyBlog/admin.py +++ b/MontyPyBlog/admin.py @@ -1,7 +1,6 @@ from django.contrib import admin from MontyPyBlog.models import Post from MontyPyBlog.models import User -from MontyPyBlog.models import Security class PostAdmin(admin.ModelAdmin): @@ -10,11 +9,14 @@ class PostAdmin(admin.ModelAdmin): 'author', 'featured_image', 'gallery_images',] # Can include methods here too (recently published, etc) list_display = ( - 'title', 'post_type', 'author') - list_filter = ( - 'author', 'post_type') + 'title', 'post_type', 'author' + ) + list_filter = [ + 'post_type', + ] search_fields = ( - 'author', 'title') + 'author', 'title' + ) # date_hierarchy = ('created_on') @@ -27,5 +29,4 @@ class PostAdmin(admin.ModelAdmin): # inlines = [PostInline] admin.site.register(Post, PostAdmin) -admin.site.register(User) -admin.site.register(Security) +admin.site.register(User) \ No newline at end of file diff --git a/MontyPyBlog/models.py b/MontyPyBlog/models.py index d38e2ea..4283449 100644 --- a/MontyPyBlog/models.py +++ b/MontyPyBlog/models.py @@ -7,11 +7,6 @@ import bson -""" -@TODO Create/research a proper json serializer for the api -""" - - # Create your models here. class Post(models.Model): title = models.CharField(max_length=255) @@ -68,9 +63,4 @@ def __unicode__(self): 'Last Login' : str(self.last_login), 'Admin' : self.is_staff, } - return self.pk - - -class Security(models.Model): - # Oauth implementation - pass + return self.pk \ No newline at end of file diff --git a/MontyPyBlog/urls.py b/MontyPyBlog/urls.py index d5ea5a9..9d23a56 100644 --- a/MontyPyBlog/urls.py +++ b/MontyPyBlog/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls import patterns, url, include +from django.conf.urls import patterns, url from MontyPyBlog import views diff --git a/MontyPyBlog/views/post.py b/MontyPyBlog/views/post.py index 5484dad..81e01cc 100644 --- a/MontyPyBlog/views/post.py +++ b/MontyPyBlog/views/post.py @@ -1,8 +1,8 @@ from django.http import HttpResponse from MontyPyBlog.models import Post, User -from django.http import Http404 +import time -from rest_framework.decorators import api_view, parser_classes +from rest_framework.decorators import api_view, parser_classes, authentication_classes, permission_classes from rest_framework.response import Response from MontyPyBlog.serializers import PostSerializer, UserSerializer from rest_framework import status @@ -13,7 +13,6 @@ import threading import imghdr -from django.views.decorators.csrf import csrf_exempt """ @TODO: @@ -34,6 +33,9 @@ def index(request): @api_view(['GET']) def get_post(request): if request.method == 'GET': + if request.auth is None: + return Response('Not Authenticated', status=status.HTTP_403_FORBIDDEN) + try: post_id = request.DATA.get('post_id') post = Post.objects.get(pk=post_id) @@ -48,6 +50,9 @@ def get_post(request): @api_view(['POST']) def post_post(request): if request.method == 'POST': + if request.auth is None: + return Response('Not Authenticated', status=status.HTTP_403_FORBIDDEN) + user = User.objects.get(pk=request.DATA.get('author')) data = { 'title': request.DATA.get('title'), @@ -74,12 +79,15 @@ def post_post(request): @api_view(['PATCH']) def patch_post(request): if request.method == 'PATCH': + if request.auth is None: + return Response('Not Authenticated', status=status.HTTP_403_FORBIDDEN) + try: post_id = request.DATA.get('post_id') post = Post.objects.get(pk=post_id) except Post.DoesNotExist: - return Http404 + return Response(status=status.HTTP_404_NOT_FOUND) user_id = request.DATA.get('author') user = User.objects.get(pk=user_id) @@ -112,6 +120,8 @@ def patch_post(request): @parser_classes((MultiPartParser, FormParser)) def post_files(request): if request.method == 'POST': + if request.auth is None: + return Response('Not Authenticated', status=status.HTTP_403_FORBIDDEN) post = Post.objects.get(pk=request.DATA.get('post_id')) @@ -165,4 +175,4 @@ def upload(file): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) else: - return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED) + return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED) \ No newline at end of file diff --git a/MontyPyBlog/views/user.py b/MontyPyBlog/views/user.py index 4ccdfd9..bfd16c3 100644 --- a/MontyPyBlog/views/user.py +++ b/MontyPyBlog/views/user.py @@ -15,6 +15,9 @@ @api_view(['POST']) def post_user(request): if request.method == 'POST': + if request.auth is None: + return Response('Not Authenticated', status=status.HTTP_403_FORBIDDEN) + data = { 'username': request.DATA.get('username'), 'email': request.DATA.get('email'), @@ -36,6 +39,9 @@ def post_user(request): @api_view(['GET']) def get_user(request): if request.method == 'GET': + if request.auth is None: + return Response('Not Authenticated', status=status.HTTP_403_FORBIDDEN) + try: user_id = request.DATA.get('user_id') user = User.objects.get(pk=user_id) @@ -59,6 +65,9 @@ def get_user(request): @api_view(['PATCH']) def patch_user(request): if request.method == 'PATCH': + if request.auth is None: + return Response('Not Authenticated', status=status.HTTP_403_FORBIDDEN) + try: user_id = request.DATA.get('user_id') user = User.objects.get(pk=user_id) From 8fc722b514bc72f668dc5ec9e80635177ed30ec9 Mon Sep 17 00:00:00 2001 From: Simon Yousoufov Date: Sat, 8 Nov 2014 16:03:33 -0500 Subject: [PATCH 09/13] Unique filenames for s3 objects --- MontyPyBlog/views/post.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/MontyPyBlog/views/post.py b/MontyPyBlog/views/post.py index 81e01cc..e230ff7 100644 --- a/MontyPyBlog/views/post.py +++ b/MontyPyBlog/views/post.py @@ -135,7 +135,7 @@ def post_files(request): accepted_file_types = ['jpeg', 'gif', 'png'] - def upload(file): + def upload(file, key_name): if imghdr.what(file) not in accepted_file_types: return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) @@ -146,7 +146,7 @@ def upload(file): bucket = s3.get_bucket(bucket_name) key_object = Key(bucket) - key_object.key = s3_folder_name + file.name + key_object.key = s3_folder_name + key_name key_object.set_contents_from_file(file) key_object.make_public() @@ -154,11 +154,13 @@ def upload(file): return image_url + # Bottleneck will be connecting to S3, so we thread # Research how to get return values for threads file_urls = [] for filename, file in request.FILES.iteritems(): - t = threading.Thread(target=upload, args=(file,)).start() - file_urls.append(os.environ['S3_BUCKET_LOCATION'] + file.name) + key_name = str(time.time()) + '-' + file.name + t = threading.Thread(target=upload, args=(file, key_name)).start() + file_urls.append(os.environ['S3_BUCKET_LOCATION'] + key_name) data = { file_upload_type: ','.join(file_urls), From 91b535b863cd193e5c114dad63586ade5c19015f Mon Sep 17 00:00:00 2001 From: Simon Yousoufov Date: Sat, 8 Nov 2014 23:44:51 -0500 Subject: [PATCH 10/13] Updated gitignore, create sample settings file, need to remove current settings file from repo --- .gitignore | 2 + cmsAPI/settings_sample.py | 189 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 191 insertions(+) create mode 100644 cmsAPI/settings_sample.py diff --git a/.gitignore b/.gitignore index a94e49d..028f82e 100644 --- a/.gitignore +++ b/.gitignore @@ -56,6 +56,8 @@ target/ # App Specific cmsAPI/appinfo.py cmsAPI/appinfo.pyc +cmsAPI/settings.py +cmsAPI/settings.pyc #IDE .idea/ \ No newline at end of file diff --git a/cmsAPI/settings_sample.py b/cmsAPI/settings_sample.py new file mode 100644 index 0000000..64aab6b --- /dev/null +++ b/cmsAPI/settings_sample.py @@ -0,0 +1,189 @@ +import appinfo +# Django settings for cmsAPI project. + +DEBUG = True +TEMPLATE_DEBUG = DEBUG + +ADMINS = ( + # ('Your Name', 'your_email@example.com'), +) + +MANAGERS = ADMINS + +DATABASES = { + 'default': { + 'ENGINE': 'django_mongodb_engine', + 'NAME': 'cms_api', + 'HOST' : '127.0.0.1', + 'PORT' : '27017', + 'USER' : 'simon', + 'PASSWORD' : 'simon', + }, +} + +# Hosts/domain names that are valid for this site; required if DEBUG is False +# See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts +ALLOWED_HOSTS = [] + +# Local time zone for this installation. Choices can be found here: +# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name +# although not all choices may be available on all operating systems. +# In a Windows environment this must be set to your system time zone. +TIME_ZONE = 'America/Chicago' + +# Language code for this installation. All choices can be found here: +# http://www.i18nguy.com/unicode/language-identifiers.html +LANGUAGE_CODE = 'en-us' + +SITE_ID = '544bf2f9438b4c5c2919c96e' + +# If you set this to False, Django will make some optimizations so as not +# to load the internationalization machinery. +USE_I18N = True + +# If you set this to False, Django will not format dates, numbers and +# calendars according to the current locale. +USE_L10N = True + +# If you set this to False, Django will not use timezone-aware datetimes. +USE_TZ = True + +# Absolute filesystem path to the directory that will hold user-uploaded files. +# Example: "/var/www/example.com/media/" +MEDIA_ROOT = '' + +# URL that handles the media served from MEDIA_ROOT. Make sure to use a +# trailing slash. +# Examples: "http://example.com/media/", "http://media.example.com/" +MEDIA_URL = '' + +FILE_UPLOAD_HANDLERS = ( + "django.core.files.uploadhandler.MemoryFileUploadHandler", + "django.core.files.uploadhandler.TemporaryFileUploadHandler" +) + +# Absolute path to the directory static files should be collected to. +# Don't put anything in this directory yourself; store your static files +# in apps' "static/" subdirectories and in STATICFILES_DIRS. +# Example: "/var/www/example.com/static/" +STATIC_ROOT = '' + +# URL prefix for static files. +# Example: "http://example.com/static/", "http://static.example.com/" +STATIC_URL = '/static/' + +# Additional locations of static files +STATICFILES_DIRS = ( + # Put strings here, like "/home/html/static" or "C:/www/django/static". + # Always use forward slashes, even on Windows. + # Don't forget to use absolute paths, not relative paths. +) + +# List of finder classes that know how to find static files in +# various locations. +STATICFILES_FINDERS = ( + 'django.contrib.staticfiles.finders.FileSystemFinder', + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', +# 'django.contrib.staticfiles.finders.DefaultStorageFinder', +) + +# Make this unique, and don't share it with anybody. +SECRET_KEY = 'rrq@(@vs34sl0h3o0yw%z#0taznk(kl&wyl-(77u$_&3&2-(&9' + +# List of callables that know how to import templates from various sources. +TEMPLATE_LOADERS = ( + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader', +# 'django.template.loaders.eggs.Loader', +) + +MIDDLEWARE_CLASSES = ( + 'corsheaders.middleware.CorsMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + # Uncomment the next line for simple clickjacking protection: + # 'django.middleware.clickjacking.XFrameOptionsMiddleware', +) + +ROOT_URLCONF = 'cmsAPI.urls' + +# Python dotted path to the WSGI application used by Django's runserver. +WSGI_APPLICATION = 'cmsAPI.wsgi.application' + +TEMPLATE_DIRS = ( + # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". + # Always use forward slashes, even on Windows. + # Don't forget to use absolute paths, not relative paths. +) + +# +# CORS +# +CORS_ORIGIN_ALLOW_ALL = False +CORS_ORIGIN_WHITELIST = ( + 'google.com', + 'hostname.example.com', +) + + +INSTALLED_APPS = ( + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.sites', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'MontyPyBlog', + 'djangotoolbox', + 'django_mongodb_engine', + 'rest_framework', + 'corsheaders', + 'provider', + 'provider.oauth2', + # Uncomment the next line to enable the admin: + 'django.contrib.admin', + # Uncomment the next line to enable admin documentation: + # 'django.contrib.admindocs', +) + +REST_FRAMEWORK = { + 'DEFAULT_AUTHENTICATION_CLASSES': ( + # 'rest_framework.authentication.BasicAuthentication', + # 'rest_framework.authentication.SessionAuthentication', + 'rest_framework.authentication.OAuth2Authentication', + ) +} + +SESSION_SERIALIZER = 'django.contrib.sessions.serializers.JSONSerializer' + +# A sample logging configuration. The only tangible logging +# performed by this configuration is to send an email to +# the site admins on every HTTP 500 error when DEBUG=False. +# See http://docs.djangoproject.com/en/dev/topics/logging for +# more details on how to customize your logging configuration. +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'filters': { + 'require_debug_false': { + '()': 'django.utils.log.RequireDebugFalse' + } + }, + 'handlers': { + 'mail_admins': { + 'level': 'ERROR', + 'filters': ['require_debug_false'], + 'class': 'django.utils.log.AdminEmailHandler' + } + }, + 'loggers': { + 'django.request': { + 'handlers': ['mail_admins'], + 'level': 'ERROR', + 'propagate': True, + }, + } +} \ No newline at end of file From 9ce00ed3ecc7d626bd7a658958ec4d74149b6357 Mon Sep 17 00:00:00 2001 From: Simon Yousoufov Date: Sun, 9 Nov 2014 09:05:13 -0500 Subject: [PATCH 11/13] Added route to get csrf token for dev --- MontyPyBlog/urls.py | 2 ++ MontyPyBlog/views/post.py | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/MontyPyBlog/urls.py b/MontyPyBlog/urls.py index 9d23a56..80ca0e1 100644 --- a/MontyPyBlog/urls.py +++ b/MontyPyBlog/urls.py @@ -24,4 +24,6 @@ url(r'^user/login/(?P\w+)/$', views.post_login, name='postLogin'), # Ex: /cms/user/logout/7/ url(r'^user/logout/(?P\w+)/$', views.post_logout, name='postLogout'), + # Ex: /cms/csrf/token/ + url(r'^csrf/token/$', views.get_csrf, name='getToken'), ) diff --git a/MontyPyBlog/views/post.py b/MontyPyBlog/views/post.py index e230ff7..2d21058 100644 --- a/MontyPyBlog/views/post.py +++ b/MontyPyBlog/views/post.py @@ -1,6 +1,7 @@ from django.http import HttpResponse from MontyPyBlog.models import Post, User import time +import django.middleware.csrf from rest_framework.decorators import api_view, parser_classes, authentication_classes, permission_classes from rest_framework.response import Response @@ -47,6 +48,15 @@ def get_post(request): return Response(serializer.data) +# For dev +@api_view(['GET']) +def get_csrf(request): + if request.method == 'GET': + return Response(django.middleware.csrf.get_token(request)) + else: + return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED) + + @api_view(['POST']) def post_post(request): if request.method == 'POST': From ed907ad4ad82e9ffcb312ab968e7be7b893654d0 Mon Sep 17 00:00:00 2001 From: Simon Yousoufov Date: Sun, 9 Nov 2014 09:15:22 -0500 Subject: [PATCH 12/13] update gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 028f82e..c4580cc 100644 --- a/.gitignore +++ b/.gitignore @@ -58,6 +58,9 @@ cmsAPI/appinfo.py cmsAPI/appinfo.pyc cmsAPI/settings.py cmsAPI/settings.pyc +admin/ +rest_framework/ +static/ #IDE .idea/ \ No newline at end of file From b93a7b3bcf807a3d4bb32d2721ba8ca6bbb7abf5 Mon Sep 17 00:00:00 2001 From: Simon Yousoufov Date: Sun, 9 Nov 2014 09:17:43 -0500 Subject: [PATCH 13/13] settings_sample --- cmsAPI/settings_sample.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmsAPI/settings_sample.py b/cmsAPI/settings_sample.py index 64aab6b..76fc129 100644 --- a/cmsAPI/settings_sample.py +++ b/cmsAPI/settings_sample.py @@ -119,9 +119,7 @@ # Don't forget to use absolute paths, not relative paths. ) -# # CORS -# CORS_ORIGIN_ALLOW_ALL = False CORS_ORIGIN_WHITELIST = ( 'google.com',