diff --git a/src/postorius/auth/decorators.py b/src/postorius/auth/decorators.py new file mode 100644 index 0000000..cc8f130 --- /dev/null +++ b/src/postorius/auth/decorators.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 1998-2012 by the Free Software Foundation, Inc. +# +# This file is part of Postorius. +# +# Postorius 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. +# +# Postorius 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 +# Postorius. If not, see . +"""Postorius view decorators.""" + + +from django.core.exceptions import PermissionDenied + +from postorius.models import (Domain, List, Member, MailmanUser, + MailmanApiError, Mailman404Error) + + +def list_owner_required(fn): + """Check if the logged in user is the list owner of the given list. + Assumes that the request object is the first arg and that fqdn_listname + is present in kwargs. + """ + def wrapper(*args, **kwargs): + user = args[0].user + fqdn_listname = kwargs['fqdn_listname'] + if not user.is_authenticated(): + raise PermissionDenied + if user.is_superuser: + return fn(*args, **kwargs) + mlist = List.objects.get_or_404(fqdn_listname=fqdn_listname) + if user.email not in mlist.members: + raise PermissionDenied + else: + return fn(*args, **kwargs) + return wrapper diff --git a/src/postorius/doc/_build/doctrees/development.doctree b/src/postorius/doc/_build/doctrees/development.doctree index dab71ed..c539fad 100644 --- a/src/postorius/doc/_build/doctrees/development.doctree +++ b/src/postorius/doc/_build/doctrees/development.doctree Binary files differ diff --git a/src/postorius/doc/_build/doctrees/environment.pickle b/src/postorius/doc/_build/doctrees/environment.pickle index 7d858f4..aab8103 100644 --- a/src/postorius/doc/_build/doctrees/environment.pickle +++ b/src/postorius/doc/_build/doctrees/environment.pickle Binary files differ diff --git a/src/postorius/doc/_build/doctrees/news.doctree b/src/postorius/doc/_build/doctrees/news.doctree index 97d15a6..9a0c7f7 100644 --- a/src/postorius/doc/_build/doctrees/news.doctree +++ b/src/postorius/doc/_build/doctrees/news.doctree Binary files differ diff --git a/src/postorius/doc/_build/html/_sources/news.txt b/src/postorius/doc/_build/html/_sources/news.txt index 75b846d..5845b46 100644 --- a/src/postorius/doc/_build/html/_sources/news.txt +++ b/src/postorius/doc/_build/html/_sources/news.txt @@ -34,6 +34,8 @@ * added developer documentation * added test helper utils * all code now conform to PEP8 +* themes: removed obsolete MAILMAN_THEME settings from templates, contexts, file structure; contributed by Richard Wackerbarth (LP: 1043258) +* added access control for list owners 1.0 alpha 1 -- "Space Farm" diff --git a/src/postorius/doc/_build/html/development.html b/src/postorius/doc/_build/html/development.html index 95d139a..9a0c4a4 100644 --- a/src/postorius/doc/_build/html/development.html +++ b/src/postorius/doc/_build/html/development.html @@ -86,65 +86,8 @@
  • Memberships
  • Addresses
  • -
    -

    Mocking mailman.client objects

    -
    -
    Domains
    -

    postorius.tests.utils.create_mock_domain creates a mock domain object:

    -
    >>> properties = dict(contact_address='postmaster@example.org',
    -...                   description='Example dot Org',
    -...                   mail_host='example.org',
    -...                   url_host='www.example.org')
    ->>> mock_domain = create_mock_domain(properties)
    ->>> print mock_domain
    -<MagicMock name='Domain' id='...'>
    ->>> print mock_domain.contact_address
    -postmaster@example.org
    ->>> print mock_domain.description
    -Example dot Org
    ->>> print mock_domain.mail_host
    -example.org
    ->>> print mock_domain.url_host
    -www.example.org
    -
    -
    -
    -
    -
    Mailing Lists
    -

    postorius.tests.utils.create_mock_list creates a mock list object:

    -
    >>> properties = dict(fqdn_listname='testlist@example.org',
    -...                   mail_host='example.org',
    -...                   list_name='testlist',
    -...                   display_name='Test List')
    ->>> mock_list = create_mock_list(properties)
    ->>> print mock_list
    -<MagicMock name='List' id='...'>
    ->>> print mock_list.fqdn_listname
    -testlist@example.org
    ->>> print mock_list.mail_host
    -example.org
    ->>> print mock_list.list_name
    -testlist
    ->>> print mock_list.display_name
    -Test List
    -
    -
    -
    -
    -
    Memberships
    -

    postorius.tests.utils.create_mock_list creates a mock list object:

    -
    >>> properties = dict(fqdn_listname='testlist@example.org',
    -...                   address='les@example.org',)
    ->>> mock_member = create_mock_member(properties)
    ->>> print mock_member
    -<MagicMock name='Member' id='...'>
    ->>> print mock_member.fqdn_listname
    -testlist@example.org
    ->>> print mock_member.address
    -les@example.org
    -
    -
    -
    +
    +

    Mocking mailman.client objects

    @@ -162,12 +105,7 @@
  • Testing diff --git a/src/postorius/doc/_build/html/genindex.html b/src/postorius/doc/_build/html/genindex.html index cf95631..1f85e6d 100644 --- a/src/postorius/doc/_build/html/genindex.html +++ b/src/postorius/doc/_build/html/genindex.html @@ -52,20 +52,9 @@

    Index

    - P - | T + T
    -

    P

    - - -
    - -
    postorius.tests.test_utils (module) -
    - -
    -

    T

    diff --git a/src/postorius/doc/_build/html/news.html b/src/postorius/doc/_build/html/news.html index fa2409e..41ca7d1 100644 --- a/src/postorius/doc/_build/html/news.html +++ b/src/postorius/doc/_build/html/news.html @@ -37,6 +37,9 @@ index
  • + modules |
  • +
  • next |
  • @@ -79,6 +82,8 @@
  • added developer documentation
  • added test helper utils
  • all code now conform to PEP8
  • +
  • themes: removed obsolete MAILMAN_THEME settings from templates, contexts, file structure; contributed by Richard Wackerbarth (LP: 1043258)
  • +
  • added access control for list owners
  • @@ -155,6 +160,9 @@ index
  • + modules |
  • +
  • next |
  • diff --git a/src/postorius/doc/_build/html/objects.inv b/src/postorius/doc/_build/html/objects.inv index 0c1d872..0fcc642 100644 --- a/src/postorius/doc/_build/html/objects.inv +++ b/src/postorius/doc/_build/html/objects.inv Binary files differ diff --git a/src/postorius/doc/_build/html/py-modindex.html b/src/postorius/doc/_build/html/py-modindex.html index aa9ec3a..0d82010 100644 --- a/src/postorius/doc/_build/html/py-modindex.html +++ b/src/postorius/doc/_build/html/py-modindex.html @@ -53,35 +53,20 @@

    Python Module Index

    - p | t
    - - - - - - - - + id="toggle-1" style="display: none" alt="-" /> - +
     
    - p
    - postorius -
        - postorius.tests.test_utils -
     
    t
    tests
        tests.tests diff --git a/src/postorius/doc/_build/html/searchindex.js b/src/postorius/doc/_build/html/searchindex.js index 8cad9b5..49da93d 100644 --- a/src/postorius/doc/_build/html/searchindex.js +++ b/src/postorius/doc/_build/html/searchindex.js @@ -1 +1 @@ -Search.setIndex({objects:{tests:{tests:[4,0,1,""]},"postorius.tests":{test_utils:[0,0,1,""]}},terms:{all:[0,4,2,3],code:[4,3],forget:4,prefil:4,test_list_set:0,four:[],ackownledg:[],runserv:2,dirnam:[],follow:[4,2],content:[1,4],decid:[],depend:[3,2],authoris:[],send:[],shall:4,granci:3,under:3,introduc:[],merchant:3,sourc:[],everi:[],string:4,without:[4,3],far:[],none:4,offlin:[],util:[0,4,3],context_processor:[],mechan:4,veri:[],exact:4,mailmanwebgsoc2011:[],testobject:4,contenttyp:[],administr:4,level:[],did:4,button:4,list:[0,4,3],liza:4,"try":4,item:4,adjust:[],localhost:4,quick:[],setup:[4,2,3],dir:[],pleas:4,modelbackend:[],impli:3,httpresponseredirect:4,cfg:[],seper:[],request:4,past:[],second:2,download:2,further:[],click:4,compat:[],index:4,what:[],name_of_permiss:[],appear:[],richard:3,sum:[],abl:[4,2],current:4,delet:4,new_list1:4,postoriu:[0,1,2,3],franziska:[],"new":[1,3,4],net:[],method:[],abov:4,gener:3,never:2,remeb:[],here:[],themself:[],ubuntu:[],path:2,along:3,modifi:[4,3],sinc:2,valu:4,search:[],mailinglist:[],vertifi:[],anymor:[],errorlog:[],step:[],jame:4,doctest:4,action:4,chang:2,mailman_media:[],mock:0,contactpag:[],via:[],appli:[],app:[4,3],sponser:[],foundat:3,releas:2,api:3,sponsel:[],foord:0,home:[],instal:[1,2,3,4],myownunittest:0,middlewar:[],from:[0,4,2],describ:4,would:[],commun:[],doubl:4,two:[],perm:[],next:[],websit:[],few:[],call:3,typo:3,recommend:[],dict:0,type:4,web_host:4,create_mock_list:0,mailman_django:[],abspath:[],relat:4,ital:[],site:[4,2],warn:[4,2],trail:[],berlio:[],stick:[],autodoc:[],particular:3,dooo:[],hold:[],unpack:[],easiest:4,dot:0,customlog:[],account:[3,2],join:[],alia:2,prepar:2,work:[],uniqu:[],dev:[3,2],itself:[],can:[0,4,2,3],purpos:3,def:[],control:[],defer:3,sqlite:2,prompt:2,login_requir:4,tar:[],process:3,sudo:2,accept:3,topic:[],want:[4,2],create_mock_memb:0,create_mock_domain:0,nearli:[],alwai:2,cours:[],multipl:[],listmembersviewtest:0,anoth:4,faulti:[],georg:4,write:[],how:2,list_nam:0,reject:3,instead:4,config:[],nasti:[],css:2,updat:4,product:2,resourc:4,farm:3,pypi:2,after:[4,2],"long":[],usabl:[],befor:4,wrong:4,mai:[],end:[],data:4,third:2,postfix:[],bind:[],bootstrap:[],credenti:[],django:[0,4,2,3],alias:3,environ:2,adverrtis:4,jain:3,enter:4,fallback:[],automaticli:[],egg:[],order:2,listnam:4,help:[],move:3,becaus:4,has_perm:[],"__future__":[],style:[],directli:4,fit:3,fix:3,browserid:3,unicode_liter:[],better:[],restart:[],onc:[],mail:[0,4,3],hidden:[],main:[],might:[],guarente:[],split:[],them:[0,2],good:[],"return":[],thei:4,python:[0,4,2],databs:[],auth:[],unfortuneatli:[],"break":[],mention:4,front:4,retreiv:0,now:[4,2,3],term:3,benst:[],somewher:2,name:[0,4],anyth:[],edit:[4,3],simpl:[],postorius_error:[],authent:[],separ:[4,3],easili:[],senarclen:3,each:4,debug:4,found:[4,2],went:4,mailman_test_bindir:[],everyth:4,domain:[0,4,3],replac:[],idea:[],procedur:[],realli:4,redistribut:3,meta:4,"static":2,connect:3,our:4,patch:[],todo:[],dependeci:[],out:3,variabl:[],shown:4,space:3,miss:3,robert:2,develop:[0,1,2,3,4],publish:3,api_us:4,profil:3,daniel:3,rest_serv:[],got:[],correct:2,earlier:[],insid:[],free:[4,3],standalon:[],reason:[],base:[],mailmanweb:[],lists_of_domain:[],put:0,org:[0,3,2],restbackend:[],"40mail":4,launch:[],could:[],latest:2,membership:[0,4],keep:2,filter:[],thing:4,place:[],isn:[],root_urlconf:[],requireti:[],summari:4,first:[4,2],softwar:3,rang:[],render:[],prevent:[],feel:4,media_root:[],natti:[],restrict:4,date:2,instruct:[4,2],alreadi:4,done:4,messag:[4,3],authentif:4,owner:4,stabl:4,installed_app:[],open:4,gpl:[],differ:2,rrze:[],my_own_test:0,benedict:3,hardcopi:[],licens:3,system:[],least:4,url_host:0,too:0,licenc:[],fullfil:[],"final":4,store:[],shell:4,option:4,real_nam:4,copi:3,specifi:4,gsoc:[],"var":[],part:4,"__test__":0,exactli:[],priveledg:[],serv:2,test_some_method:[],provid:[4,2,3],remov:[4,3],new_domain:[],project:[0,3,2],postorius_standalon:2,were:4,posit:4,minut:[],other:4,fqdn_listnam:[0,4],pre:[],sai:[],runner:0,ani:3,postorius_access:[],packag:2,have:[4,2,3],tabl:2,need:[4,2],element:[],wsgiscriptalia:2,engin:2,inform:2,florian:[],destroi:[],self:[],note:[4,2],also:2,exampl:[0,4],take:[],indic:4,combin:[],mock_memb:0,singl:4,even:3,sure:[0,4,2],kati:4,allow:[4,2],httpredirectobject:4,usernam:[],object:[0,4],asdasd:[],most:4,plan:[],letter:4,watt:4,alpha:3,"class":[],icon:[],collectstat:2,don:4,bzr:2,url:4,doc:[],later:[],cover:2,temporili:4,doe:4,deni:2,pars:4,usual:[],dev_setup:[],wsgi:2,show:[4,3],text:4,contact_address:0,session:4,permiss:4,corner:[],fine:[],find:0,magicmock:0,redirect:4,absolut:[],onli:[4,2],eas:[],locat:2,launchpad:2,copyright:3,explain:4,configur:[],apach:[3,2],should:[0,4,2,3],version:[3,2],suppos:[],templat:3,folder:[0,2],local:[4,2],hope:3,media_url:[],hit:[],contribut:3,get:[4,2],"__file__":[],stop:4,obviou:[],absolute_import:[],csrf:3,subscript:4,requir:4,mail_host:[0,4],bar:[],template_dir:[],"public":3,reload:2,bad:[],common:[],restadmin:[],mm_membership:4,where:0,view:[4,3],wiki:[],conform:3,set:[0,4,2,3],special:[],sometest:[],see:[4,2,3],domain_admin:[],result:[],respons:4,testcas:[],wonder:[],awar:4,statu:4,mailman3a7:[],correctli:[],databas:[4,2],someth:4,test_list_memb:0,behind:[],quickest:2,between:4,"import":[0,4],awai:[],email:4,realnam:[],extens:[],correclti:[],advertis:4,subfold:[],thank:3,both:[],last:4,plugin:[],admin:2,howev:[],etc:2,instanc:4,context:[],delete_list:4,logout:[],login:[4,3],com:4,load:4,english:4,simpli:4,point:[],instanti:[],overview:4,unittest:[],address:[0,4],virtualhost:2,mock_list:0,header:[],featur:[],non:[],shortcut:0,linux:4,guid:[4,2],assum:2,backend:[],quit:[],trunk:[],mailman:[0,1,2,3,4],coupl:4,"0a7":[],been:4,compon:[],much:[],unsubscrib:[4,3],modif:[],addit:[],quickli:0,upcom:[],imag:2,xxx:[],togeth:4,i18n:[],ngeorg:4,niederreit:2,those:4,"case":4,creativecommon:[],therefor:[],look:[],gnu:[3,2],plain:[],align:[],properti:0,lesser:3,"while":2,dashboard:4,publicli:2,error:3,everyon:[],authentication_backend:[],new_list:[],helper:3,almost:2,demo:[],metric:3,list_own:4,archiv:4,revis:[],"__init__":0,subscrib:[4,3],decor:4,let:4,welcom:[],author:[],receiv:3,media:[],make:[0,4,2],belong:4,same:4,member:[0,3],handl:[],html:[],gui:4,document:[3,2],mod_wsgi:[3,2],behav:0,finish:4,http:[4,2,3],webserv:2,upon:[],effect:[],moment:[],http_host:4,initi:3,mani:3,implement:3,expand:[],appropri:[],moder:3,scenario:[],framework:[],api_pass:4,well:[4,2],membership_set:[],pep8:3,client:[0,4,2],command:[],thi:[0,4,2,3],choos:4,fail:[4,3],left:[],summer:3,just:2,rest:4,mailman3:4,webui:[],test_util:0,yet:[],languag:4,web:[1,2,3],display_nam:0,easi:[],project_path:[],had:[],list_summari:4,littl:0,apache2:[],add:[0,4,3],valid:4,lawrenc:[],save:[],modul:[0,4],build:[],bin:[],applic:2,which:[4,2],stein:3,unter:[],read:[],prefer:2,testlist:0,test:[0,4,3],know:2,gsoc_mailman:[],press:4,cooki:[],bit:0,password:[],tweak:[],authbackend:[],like:0,resid:[0,2],template_context_processor:[],success:[4,3],changelog:[1,3],restpass:[],server:[3,2],collect:2,href:[],setup_mm:4,either:4,page:4,www:[0,3],right:[],acknowledg:[],creation:4,some:[0,4],back:[],proper:3,create_list:[],funcit:[],librari:0,distribut:3,basic:3,buildout:2,djangoproject:[],confirm:4,avoid:[],woun:[],token:3,select:[],slash:[],necessari:2,foo:[],anna:3,refer:4,machin:4,core:[],who:[],run:[0,4,2],bold:[],symlink:[],host:[],repositori:[],post:4,bazaar:2,mm_new_domain:[],stage:[],src:0,about:[],central:[],usa:4,mass_subscrib:4,rohan:3,side:[],permission_requir:[],srv:2,act:2,fals:4,discard:3,readi:2,processor:[],block:4,own:[0,4],addus:[],status_cod:4,pythonpath:2,xyz:[],within:4,someviewclass:[],easy_instal:2,warranti:3,creativ:[],empti:4,contrib:[],your:[0,2],manag:[0,4,2],choosen:4,span:4,log:4,wai:[4,2],"40exampl":4,execut:4,print:[0,4],submit:4,custom:[],avail:[4,2],start:[4,2],reli:[],interfac:[3,2],includ:4,suit:4,superus:2,systers_django:[],"function":4,head:[],form:[4,3],offer:4,descrip:[],requestfactori:[],link:4,translat:4,teardown_mm:4,branch:[3,2],line:[4,2],"true":4,info:3,pull:2,succe:4,made:4,render_mailman_them:[],bullet:[],possibl:2,whether:4,access:[4,3],displai:4,below:4,memebership:[],otherwis:[],more:3,postmast:0,extend_ajax:[],proud:[],creat:[0,4,2],hardcod:[],dure:[4,3],doesn:[],listsettingsviewtest:0,exist:[],file:[3,2],syncdb:2,pip:2,wackerbarth:3,check:[4,3],acl:[],again:[],coder:2,successfulli:[],googl:3,titl:[],user:[0,4,2,3],when:[],detail:[3,2],gettext:4,"default":[],mizyrycki:3,mock_list2:[],futur:[],rememb:[],writabl:2,you:[0,4,2,3],servernam:[],nice:4,michael:0,why:4,prequir:[],consid:[],stai:[],outdat:4,sphinx:[],mock_domain:0,directori:[0,4,2],enjoi:4,bottom:[],descript:[0,4],rule:[],mailman_them:[],mass:[4,3],came:[],time:[4,2],escap:4,inc:3},objtypes:{"0":"py:module"},titles:["Development","Postorius - The New Mailman Web UI","Installation","News / Changelog","Using the Django App - Developers Resource (outdated)"],objnames:{"0":["py","module","Python module"]},filenames:["development","index","setup","news","using"]}) \ No newline at end of file +Search.setIndex({objects:{tests:{tests:[3,0,1,""]}},terms:{all:[0,4,2,3],code:[4,3],forget:3,prefil:3,test_list_set:0,four:[],ackownledg:[],runserv:2,dirnam:[],follow:[3,2],profil:4,decid:[],depend:[4,2],authoris:[],mailman_them:4,descript:3,send:[],granci:4,under:4,introduc:[],merchant:4,sourc:[],everi:[],string:3,far:[],authbackend:[],none:3,offlin:[],util:[0,4,3],context_processor:[],mechan:3,veri:[],exact:3,foo:[],contenttyp:[],administr:3,level:[],did:3,button:3,list:[0,4,3],contact_address:[],"try":3,item:3,adjust:[],anna:4,quick:[],setup:[4,2,3],dir:[],pleas:3,modelbackend:[],impli:4,core:[],cfg:[],seper:[],request:3,past:[],second:2,download:2,further:[],click:3,compat:[],index:3,what:[],name_of_permiss:[],appear:[],richard:4,sum:[],abl:[3,2],current:3,delet:3,new_list1:3,postoriu:[0,1,2,4],franziska:[],"new":[1,3,4],net:[],method:[],superus:2,publicli:2,gener:4,never:2,remeb:[],here:[],themself:[],ubuntu:[],path:2,along:4,modifi:[4,3],sinc:2,valu:3,search:[],mailinglist:[],vertifi:[],anymor:[],errorlog:[],host:[],later:[],jame:3,doctest:3,action:3,chang:2,mailman_media:[],element:[],contactpag:[],via:[],appli:[],app:[4,3],sponser:[],foundat:4,put:0,api:4,sponsel:[],instal:[1,2,3,4],myownunittest:0,middlewar:[],from:[0,4,2,3],describ:3,would:[],commun:[],doubl:3,two:[],perm:[],next:[],websit:[],few:[],call:4,typo:4,recommend:[],suppos:[],type:3,web_host:3,create_mock_list:[],mailman_django:[],abspath:[],relat:3,ital:[],list_own:3,warn:[3,2],trail:[],berlio:[],stick:[],autodoc:[],particular:4,dooo:[],hold:[],unpack:[],easiest:3,correctli:[],customlog:[],account:[4,2],join:[],alia:2,prepar:2,work:[],uniqu:[],dev:[4,2],mock_list2:[],archiv:3,can:[0,4,2,3],purpos:4,def:[],control:4,defer:4,sqlite:2,prompt:2,login_requir:3,tar:[],process:4,sudo:2,accept:4,topic:[],want:[3,2],create_mock_memb:[],yet:[],create_mock_domain:[],contribut:4,alwai:2,cours:[],multipl:[],permission_requir:[],anoth:3,faulti:[],georg:3,write:[],how:2,list_nam:[],reject:4,instead:3,simpl:[],"__file__":[],css:2,updat:3,product:2,resourc:3,farm:4,stop:3,earlier:[],usabl:[],befor:3,membership:[0,3],date:2,end:[],data:3,third:2,postfix:[],bind:[],bootstrap:[],credenti:[],django:[0,4,2,3],alias:4,environ:2,adverrtis:3,jain:4,enter:3,fallback:[],automaticli:[],egg:[],order:2,listnam:3,help:[],move:4,becaus:3,has_perm:[],own:[0,3],left:[],style:[],directli:3,fit:4,fix:4,browserid:4,unicode_liter:[],better:[],requir:3,onc:[],mail:[0,4,3],hidden:[],main:[],might:[],guarente:[],them:[0,2],good:[],"return":[],thei:3,python:[0,3,2],databs:[],auth:[],unfortuneatli:[],"break":[],mention:3,front:3,bar:[],now:[4,2,3],term:4,benst:[],somewher:2,name:3,anyth:[],edit:[4,3],config:[],postorius_error:[],authent:[],separ:[4,3],easili:[],senarclen:4,each:3,debug:3,found:[3,2],went:3,mailman_test_bindir:[],everyth:3,domain:[0,4,3],replac:[],idea:[],procedur:[],realli:3,line:[3,2],redistribut:4,meta:3,"static":2,connect:4,our:3,todo:[],dependeci:[],out:4,variabl:[],shown:3,space:4,miss:4,robert:2,develop:[0,1,2,3,4],publish:4,your:[0,2],content:[1,3],daniel:4,rest_serv:[],got:[],correct:2,after:[3,2],insid:[],free:[4,3],standalon:[],reason:[],base:[],mailmanweb:[],lists_of_domain:[],releas:2,org:[4,2],"40mail":3,launch:[],could:[],wrong:3,keep:2,filter:[],thing:3,place:[],isn:[],root_urlconf:[],requireti:[],view:[4,3],first:[3,2],softwar:4,rang:[],woun:[],render:[],prevent:[],feel:3,media_root:[],system:[],restrict:3,mai:[],instruct:[3,2],alreadi:3,done:3,least:3,authentif:3,owner:[4,3],stabl:3,installed_app:[],open:3,gpl:[],differ:2,rrze:[],my_own_test:0,benedict:4,hardcopi:[],licens:4,natti:[],messag:[4,3],url_host:[],too:0,licenc:[],fullfil:[],"final":3,store:[],shell:3,option:3,real_nam:3,"public":4,copi:4,specifi:3,gsoc:[],part:3,"__test__":0,exactli:[],priveledg:[],serv:2,test_some_method:[],provid:[4,2,3],remov:[4,3],structur:4,new_domain:[],project:[0,4,2],postorius_standalon:2,were:3,posit:3,minut:[],fqdn_listnam:3,pre:[],descrip:[],sai:[],runner:0,ani:4,postorius_access:[],packag:2,gui:3,have:[4,2,3],tabl:2,need:[3,2],foord:0,wsgiscriptalia:2,engin:2,inform:2,florian:[],destroi:[],self:[],client:[0,3,2],note:[3,2],also:2,exampl:3,take:[],indic:3,combin:[],mock_memb:[],singl:3,even:4,sure:[0,3,2],kati:3,allow:[3,2],httpredirectobject:3,usernam:[],object:[0,3],asdasd:[],most:3,plan:[],letter:3,watt:3,alpha:4,choos:3,"class":[],icon:[],collectstat:2,don:3,bzr:2,url:3,doc:[],proud:[],cover:2,temporili:3,doe:3,deni:2,pars:3,usual:[],dev_setup:[],wsgi:2,show:[4,3],text:3,liza:3,session:3,permiss:3,corner:[],fine:[],find:0,redirect:3,absolut:[],onli:[3,2],submit:3,locat:2,launchpad:2,copyright:4,explain:3,configur:[],apach:[4,2],should:[0,4,2,3],theme:4,version:[4,2],dict:[],templat:4,folder:[0,2],local:[3,2],hope:4,media_url:[],hit:[],nearli:[],get:[3,2],nasti:[],pypi:2,obviou:[],rest:3,csrf:4,awai:[],restart:[],retreiv:0,template_dir:[],patch:[],reload:2,bad:[],common:[],restadmin:[],mm_membership:3,where:0,summari:3,wiki:[],conform:4,set:[0,4,2,3],sometest:[],see:[4,2,3],domain_admin:[],result:[],respons:3,testcas:[],wonder:[],awar:3,statu:3,error:4,magicmock:[],databas:[3,2],someth:3,test_list_memb:0,behind:[],quickest:2,between:3,"import":[0,3],subscript:3,email:3,realnam:[],coder:2,extens:[],correclti:[],advertis:3,subfold:[],thank:4,both:[],last:3,plugin:[],admin:2,howev:[],etc:2,instanc:3,context:4,delete_list:3,logout:[],login:[4,3],com:3,load:3,simpli:3,author:[],point:[],instanti:[],overview:3,unittest:[],address:[0,3],mock_list:[],header:[],non:[],littl:0,linux:3,guid:[3,2],assum:2,backend:[],quit:[],trunk:[],mailman:[0,1,2,3,4],coupl:3,"0a7":[],been:3,compon:[],much:[],unsubscrib:[4,3],modif:[],futur:[],addit:[],quickli:0,upcom:[],imag:2,xxx:[],togeth:3,rememb:[],i18n:[],ngeorg:3,niederreit:2,those:3,"case":3,creativecommon:[],therefor:[],look:[],gnu:[4,2],plain:[],align:[],properti:[],lesser:4,"while":2,dashboard:3,abov:3,mail_host:3,everyon:[],authentication_backend:[],new_list:[],helper:4,almost:2,demo:[],metric:4,site:[3,2],itself:[],revis:[],"__init__":0,decor:3,let:3,welcom:[],obsolet:4,receiv:4,media:[],make:[0,3,2],belong:3,same:3,member:[0,4],handl:[],html:[],split:[],document:[4,2],mod_wsgi:[4,2],behav:0,finish:3,http:[4,2,3],webserv:2,upon:[],effect:[],moment:[],http_host:3,initi:4,mani:4,implement:4,expand:[],appropri:[],moder:4,scenario:[],framework:[],api_pass:3,well:[3,2],membership_set:[],pep8:4,without:[4,3],command:[],thi:[0,4,2,3],english:3,fail:[4,3],latest:2,summer:4,just:2,absolute_import:[],mailman3:3,webui:[],test_util:0,restbackend:[],languag:3,web:[1,2,4],display_nam:[],easi:[],project_path:[],had:[],list_summari:3,shortcut:0,apache2:[],add:[0,4,3],valid:3,lawrenc:[],save:[],modul:[0,3],build:[],bin:[],applic:2,which:[3,2],stein:4,unter:[],read:[],prefer:2,testlist:[],writabl:2,know:2,gsoc_mailman:[],press:3,cooki:[],bit:0,password:[],tweak:[],mailmanwebgsoc2011:[],template_context_processor:[],resid:[0,2],like:0,success:[4,3],changelog:[1,4],restpass:[],server:[4,2],collect:2,href:[],setup_mm:3,either:3,page:3,www:4,right:[],acknowledg:[],creation:3,some:[0,3],back:[],proper:4,create_list:[],funcit:[],librari:0,distribut:4,basic:4,buildout:2,djangoproject:[],confirm:3,avoid:[],shall:3,token:4,servernam:[],select:[],slash:[],necessari:2,testobject:3,localhost:3,refer:3,machin:3,httpresponseredirect:3,who:[],run:[0,3,2],bold:[],symlink:[],step:[],repositori:[],post:3,bazaar:2,mm_new_domain:[],stage:[],src:0,about:[],central:[],usa:3,mass_subscrib:3,rohan:4,side:[],listmembersviewtest:0,srv:2,act:2,fals:3,discard:4,processor:[],block:3,"__future__":[],addus:[],status_cod:3,pythonpath:2,xyz:[],within:3,someviewclass:[],easy_instal:2,warranti:4,creativ:[],empti:3,contrib:[],api_us:3,manag:[0,3,2],choosen:3,span:3,log:3,wai:[3,2],"40exampl":3,execut:3,print:3,"long":[],custom:[],avail:[3,2],start:[3,2],reli:[],interfac:[4,2],includ:3,suit:3,"var":[],systers_django:[],"function":3,head:[],form:[4,3],offer:3,subscrib:[4,3],requestfactori:[],link:3,translat:3,teardown_mm:3,eas:[],"true":3,info:4,pull:2,succe:3,made:3,render_mailman_them:[],mock_domain:[],possibl:2,whether:3,access:[4,3],displai:3,directori:[0,3,2],below:3,memebership:[],otherwis:[],more:4,postmast:[],extend_ajax:[],featur:[],creat:[0,3,2],hardcod:[],dure:[4,3],doesn:[],listsettingsviewtest:0,exist:[],file:[4,2],syncdb:2,pip:2,wackerbarth:4,check:[4,3],acl:[],again:[],readi:2,successfulli:[],googl:4,titl:[],user:[0,4,2,3],when:[],detail:[4,2],gettext:3,"default":[],mizyrycki:4,other:3,special:[],branch:[4,2],test:[0,4,3],you:[0,4,2,3],mock:0,nice:3,michael:0,why:3,prequir:[],consid:[],stai:[],outdat:3,sphinx:[],bullet:[],home:[],came:[],enjoi:3,bottom:[],virtualhost:2,rule:[],mailman3a7:[],mass:[4,3],dot:[],time:[3,2],escap:3,inc:4},objtypes:{"0":"py:module"},titles:["Development","Postorius - The New Mailman Web UI","Installation","Using the Django App - Developers Resource (outdated)","News / Changelog"],objnames:{"0":["py","module","Python module"]},filenames:["development","index","setup","using","news"]}) \ No newline at end of file diff --git a/src/postorius/doc/news.rst b/src/postorius/doc/news.rst index 63d5c46..5845b46 100644 --- a/src/postorius/doc/news.rst +++ b/src/postorius/doc/news.rst @@ -35,6 +35,7 @@ * added test helper utils * all code now conform to PEP8 * themes: removed obsolete MAILMAN_THEME settings from templates, contexts, file structure; contributed by Richard Wackerbarth (LP: 1043258) +* added access control for list owners 1.0 alpha 1 -- "Space Farm" diff --git a/src/postorius/tests/__init__.py b/src/postorius/tests/__init__.py index e09dc1e..d81551e 100644 --- a/src/postorius/tests/__init__.py +++ b/src/postorius/tests/__init__.py @@ -17,9 +17,11 @@ from postorius.tests import test_utils from postorius.tests.test_list_members import * +from postorius.tests.test_auth_decorators import * __test__ = { "Test Utils": test_utils, "List members": ListMembersViewTest, + "List Owner Required": ListOwnerRequiredTest, #"Doctest": tests, } diff --git a/src/postorius/tests/test_auth_decorators.py b/src/postorius/tests/test_auth_decorators.py new file mode 100644 index 0000000..7a358dc --- /dev/null +++ b/src/postorius/tests/test_auth_decorators.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2012 by the Free Software Foundation, Inc. +# +# This file is part of Postorius. +# +# Postorius 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. +# Postorius 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 +# Postorius. If not, see . + +from django.contrib.auth.models import AnonymousUser, User +from django.core.exceptions import PermissionDenied +from django.utils import unittest +from mock import patch + +from postorius.auth.decorators import list_owner_required +from postorius.models import (Domain, List, Member, MailmanUser, + MailmanApiError, Mailman404Error) +from mailman.client import Client + + +@list_owner_required +def dummy_function(request, fqdn_listname): + return True + + +class ListOwnerRequiredTest(unittest.TestCase): + """Tests the list_owner_required auth decorator.""" + + def setUp(self): + from django.test.client import RequestFactory + from postorius.tests.utils import create_mock_list + self.request_factory = RequestFactory() + # create a mock list with members + list_name = 'foolist@example.org' + list_id = 'foolist.example.org' + self.mock_list = create_mock_list(dict( + fqdn_listname=list_name, + list_id=list_id)) + + @patch.object(Client, 'get_list') + def test_not_authenticated(self, mock_get_list): + """Should raise PermissionDenied if user is not authenticated.""" + mock_get_list.return_value = self.mock_list + request = self.request_factory.get('/lists/foolist@example.org/' + 'settings/') + request.user = AnonymousUser() + self.assertRaises(PermissionDenied, dummy_function, request, + fqdn_listname='foolist@example.org') + + @patch.object(Client, 'get_list') + def test_superuser(self, mock_get_list): + """Should call the dummy method, if user is superuser.""" + mock_get_list.return_value = self.mock_list + request = self.request_factory.get('/lists/foolist@example.org/' + 'settings/') + request.user = User.objects.create_superuser('su1', 'su@sodo.org', + 'pwd') + return_value = dummy_function(request, + fqdn_listname='foolist@example.org') + self.assertEqual(return_value, True) + + @patch.object(Client, 'get_list') + def test_non_list_owner(self, mock_get_list): + """Should raise PermissionDenied user is not a list owner.""" + # prepare mock list object + self.mock_list.members = ['geddy@rush.it'] + mock_get_list.return_value = self.mock_list + # prepare request + request = self.request_factory.get('/lists/foolist@example.org/' + 'settings/') + request.user = User.objects.create_user('les c', 'les@primus.org', + 'pwd') + self.assertRaises(PermissionDenied, dummy_function, request, + fqdn_listname='foolist@example.org') + + @patch.object(Client, 'get_list') + def test_non_list_owner(self, mock_get_list): + """Should raise PermissionDenied user is not a list owner.""" + # prepare mock list object + self.mock_list.members = ['les@primus.org'] + mock_get_list.return_value = self.mock_list + # prepare request + request = self.request_factory.get('/lists/foolist@example.org/' + 'settings/') + request.user = User.objects.create_user('les cl', 'les@primus.org', + 'pwd') + return_value = dummy_function(request, + fqdn_listname='foolist@example.org') + self.assertEqual(return_value, True) diff --git a/src/postorius/tests/test_list_members.py b/src/postorius/tests/test_list_members.py index a23eb69..b8429b1 100644 --- a/src/postorius/tests/test_list_members.py +++ b/src/postorius/tests/test_list_members.py @@ -16,6 +16,7 @@ # Postorius. If not, see . from django.contrib.auth.models import AnonymousUser, User +from django.core.exceptions import PermissionDenied from django.utils import unittest from mock import patch @@ -61,20 +62,19 @@ '/lists/foolist@example.org/members/') # anonymous users should be redirected request.user = AnonymousUser() - response = ListMembersView.as_view()(request, - 'foolist@example.org') - self.assertEqual(response.status_code, 302) + self.assertRaises(PermissionDenied, ListMembersView.as_view(), + request, fqdn_listname='foolist@example.org') # logged in users should be redirected request.user = User.objects.create_user('les', 'les@primus.org', 'pwd') - response = ListMembersView.as_view()(request, - 'foolist@example.org') - self.assertEqual(response.status_code, 302) + self.assertRaises(PermissionDenied, ListMembersView.as_view(), + request, fqdn_listname='foolist@example.org') # superusers should get the page request.user = User.objects.create_superuser('su', 'su@sodo.org', 'pwd') - response = ListMembersView.as_view()(request, - 'foolist@example.org') + response = ListMembersView.as_view()( + request, + fqdn_listname='foolist@example.org') self.assertEqual(response.status_code, 200) def tearDown(self): diff --git a/src/postorius/views.py b/src/postorius/views.py index d8db9d8..48c401c 100644 --- a/src/postorius/views.py +++ b/src/postorius/views.py @@ -40,9 +40,10 @@ from django.views.generic import TemplateView from mailman.client import Client -from models import (Domain, List, Member, MailmanUser, MailmanApiError, - Mailman404Error) -from forms import * +from postorius.models import (Domain, List, Member, MailmanUser, + MailmanApiError, Mailman404Error) +from postorius.forms import * +from postorius.auth.decorators import list_owner_required from urllib2 import HTTPError @@ -101,8 +102,7 @@ def get_list(self, fqdn_listname): return List.objects.get_or_404(fqdn_listname=fqdn_listname) - @method_decorator(login_required(login_url='/accounts/login/')) - @method_decorator(user_passes_test(lambda u: u.is_superuser)) + @method_decorator(list_owner_required) def get(self, request, fqdn_listname): return render_to_response('postorius/lists/members.html', {'list': self.get_list(fqdn_listname)}, @@ -193,7 +193,7 @@ context_instance=RequestContext(request)) -@user_passes_test(lambda u: u.is_superuser) +@list_owner_required def list_metrics(request, fqdn_listname=None, option=None, template='postorius/lists/metrics.html'): """ @@ -386,8 +386,7 @@ context_instance=RequestContext(request)) -@login_required -@user_passes_test(lambda u: u.is_superuser) +@list_owner_required def list_delete(request, fqdn_listname): """Deletes a list but asks for confirmation first. """ @@ -411,7 +410,7 @@ context_instance=RequestContext(request)) -@user_passes_test(lambda u: u.is_superuser) +@list_owner_required def list_held_messages(request, fqdn_listname): """Shows a list of held messages. """ @@ -424,7 +423,7 @@ context_instance=RequestContext(request)) -@user_passes_test(lambda u: u.is_superuser) +@list_owner_required def accept_held_message(request, fqdn_listname, msg_id): """Accepts a held message. """ @@ -440,7 +439,7 @@ return redirect('list_held_messages', the_list.fqdn_listname) -@user_passes_test(lambda u: u.is_superuser) +@list_owner_required def discard_held_message(request, fqdn_listname, msg_id): """Accepts a held message. """ @@ -456,7 +455,7 @@ return redirect('list_held_messages', the_list.fqdn_listname) -@user_passes_test(lambda u: u.is_superuser) +@list_owner_required def defer_held_message(request, fqdn_listname, msg_id): """Accepts a held message. """ @@ -472,7 +471,7 @@ return redirect('list_held_messages', the_list.fqdn_listname) -@user_passes_test(lambda u: u.is_superuser) +@list_owner_required def reject_held_message(request, fqdn_listname, msg_id): """Accepts a held message. """ @@ -488,8 +487,7 @@ return redirect('list_held_messages', the_list.fqdn_listname) -@login_required -@user_passes_test(lambda u: u.is_superuser) +@list_owner_required def list_settings(request, fqdn_listname=None, visible_section=None, visible_option=None, template='postorius/lists/settings.html'): @@ -558,8 +556,7 @@ context_instance=RequestContext(request)) -@login_required -@user_passes_test(lambda u: u.is_superuser) +@list_owner_required def mass_subscribe(request, fqdn_listname=None, template='postorius/lists/mass_subscribe.html'): """