Re: userrights for localfs_command - verbose security unauthorized exception
Thorsten Weber <[email protected]> Fri, 20 Apr 2012 09:50:12 +0200
| Newsgroups | gmane.comp.cms.zms.devel |
|---|---|
| Message-ID | <[email protected]> |
--Apple-Mail=_96B2F2FC-99A9-48FA-A8DC-3B891D2BE209
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain;
charset=windows-1252
Hi Sascha,
1) FS_home.xml is a dtml-methode which generates a xml-file to tell a flash=
app to load external resources
in the backend there are various container object where links to special ob=
jects can be added
from these special objects details like text, images or videos are retrieve=
d in a loop (<dtml-in "getObjChildren('tabs',REQUEST)">)
in order to minimize the render- and/or load time of the xml onChangeObjEve=
nt is called with a pre-render param to tell FS_home.xml to be rendered int=
o local file system. when FS_home.xml is called from the frontend, when it =
is loaded from squid =3D> apache =3D> file-system.
2) we use http_import to get the rendered source code of FS_home.xml.=20
within the source code the substring of the backend url is striped out.
afterwards localfs_write with mode=3Db writes the codes into path + filenam=
e.
I dont use http_import often - only if it is necessary to process/parse the=
source code of a external resource.
3) yes. they are in the same scope but - as mentioned above - we are trying=
to reduce load time by this.
4) to port onChangeObjEvent as python script could be a way ...=20
but as we do not trigger security related stuff (like creating objects) it =
might be ok to set proxy role.=20
NO?
Or is it still a security risk?
Maybe you can explain a bit more how such a script could be abused?=20
Is there - in theory - a way like XSS or Code injection?
regards, Thorsten
Am 19.04.2012 um 18:04 schrieb Sascha Gottfried:
> Hi Thorsten,
>=20
> 1) please add the source code for http://myObj_url/FS_home.xml
> Is it a more python script or DTML method?
>=20
> 2) Explain your need for using http_import()! Did you often used this met=
hod before? Try to explain why it is not possible with plain DTML tags. Loo=
k at the sample code below
>=20
> onChangeObjEvt() explained:=20
> http://www.zms-publishing.com/dokumentation/literatur/professional_manual=
/api_tricks/onchangeobjevt/index_ger.html
>=20
> 3) Switch to a simple call of whatever FS_Home.xml is and pass parameters=
appropriately. This should be possible because FS_Home.xml and onChangeObj=
Evt() are in the scope of the same special object definition. Right?
>=20
> 4)What about implementing onChangeObjEvt() in python?=20
>=20
> See you later!
> Sascha
>=20
> --- In [email protected], Thorsten Weber <tweber@...> wrote:
> >
> > Hi Sascha,
> >=20
> > first of all - thank you so much for your efforts and explanations!=20
> > this really opens a door for me...
> > my first post was more a guess than knowledge ...=20
> > based on what the behaviors we observed (it worked for admin, not for Z=
MSAuthors).
> >=20
> > so I tried my best to provide a traceback as required ... but i am afra=
id i fail to do so.
> >=20
> > anyway ...=20
> >=20
> > i took away the proxy role manager from the methode myObj_meta_id.onCha=
ngeObjEvent
> >=20
> > this is what the methode looks like:
> >=20
> > <dtml-comment> -- // prerender FS_Home.xml from myObj // -- </dtml-comm=
ent>
> > <dtml-let xml_text=3D"http_import('http://myObj_url/FS_home.xml?prerend=
er=3Dtrue&rand=3D'+ZopeTime().strftime('%Y%m%d%H%M%S'))"
> > path=3D"'/srv/project/virtualenv/xml/'">
> >=20
> > <dtml-if xml_text>
> > <dtml-comment> -- // write FS_Home.xml to disk // -- </dtml-comment>
> > <dtml-call "localfs_write(path+'FS_home.xml',xml_text,mode=3D'b')">
> > </dtml-if>
> >=20
> > <dtml-comment> -- // remove FS_Home.xml from Squid cache // -- </dtml-c=
omment>
> > <dtml-call "localfs_command('/usr/bin/squidclient -h 85.25.124.231 -p 8=
0 -m PURGE http://myObj_url/FS_home.xml')">
> >=20
> > </dtml-let>
> >=20
> > ZMS.localfs_read is granted for path.
> >=20
> > and called the methode with user credentials of ZMSAuthor (HTTP_Auth wi=
th IE8 Browser and with Firefox/MacOSX)
> >=20
> > and this is what i find in event_log:
> >=20
> > ------
> > 2012-04-19T15:11:29 ERROR Zope.SiteErrorLog 1334841089.320.269759942749=
http://my_url/content/metaobj_manager/myObj_meta_id.onChangeObjEvt
> > Traceback (innermost last):
> > Module ZPublisher.Publish, line 116, in publish
> > Module ZPublisher.BaseRequest, line 609, in traverse
> > Module ZPublisher.HTTPResponse, line 718, in unauthorized
> > Unauthorized: <strong>You are not authorized to access this resource.</=
strong><p>
> > No Authorization header found.</p>
> > ------
> > 2012-04-19T15:11:29 ERROR root Exception while rendering an error messa=
ge
> > Traceback (most recent call last):
> > File "/srv/my_url/virtualenv/zope/lib/python2.5/site-packages/Zope2-2.1=
2.3-py2.5-linux-x86_64.egg/OFS/SimpleItem.py", line 287, in raise_standardE=
rrorMessage
> > v =3D s(client, REQUEST, **kwargs)
> > File "/srv/my_url/virtualenv/zope/lib/python2.5/site-packages/Zope2-2.1=
2.3-py2.5-linux-x86_64.egg/OFS/DTMLMethod.py", line 149, in __call__
> > r =3D apply(HTML.__call__, (self, client, REQUEST), kw)
> > File "/srv/my_url/virtualenv/zope/lib/python2.5/site-packages/Zope2-2.1=
2.3-py2.5-linux-x86_64.egg/DocumentTemplate/DT_String.py", line 477, in __c=
all__
> > try: result =3D render_blocks(self._v_blocks, md)
> > File "/srv/my_url/virtualenv/zope/lib/python2.5/site-packages/Zope2-2.1=
2.3-py2.5-linux-x86_64.egg/DocumentTemplate/DT_Util.py", line 202, in eval
> > return eval(code, d)
> > File "<string>", line 1, in <expression>
> > File "/srv/my_url/virtualenv/zope/lib/python2.5/site-packages/Zope2-2.1=
2.3-py2.5-linux-x86_64.egg/OFS/DTMLMethod.py", line 149, in __call__
> > r =3D apply(HTML.__call__, (self, client, REQUEST), kw)
> > File "/srv/my_url/virtualenv/zope/lib/python2.5/site-packages/Zope2-2.1=
2.3-py2.5-linux-x86_64.egg/DocumentTemplate/DT_String.py", line 477, in __c=
all__
> > try: result =3D render_blocks(self._v_blocks, md)
> > File "/srv/my_url/virtualenv/zope/lib/python2.5/site-packages/Zope2-2.1=
2.3-py2.5-linux-x86_64.egg/OFS/DTMLMethod.py", line 140, in __call__
> > r =3D apply(HTML.__call__, (self, client, REQUEST), kw)
> > File "/srv/my_url/virtualenv/zope/lib/python2.5/site-packages/Zope2-2.1=
2.3-py2.5-linux-x86_64.egg/DocumentTemplate/DT_String.py", line 477, in __c=
all__
> > try: result =3D render_blocks(self._v_blocks, md)
> > File "/srv/my_url/virtualenv/zope/lib/python2.5/site-packages/Zope2-2.1=
2.3-py2.5-linux-x86_64.egg/DocumentTemplate/DT_Util.py", line 202, in eval
> > return eval(code, d)
> > File "<string>", line 1, in <expression>
> > NameError: name 'lang' is not defined
> >=20
> >=20
> > Me (and probably you) would have expected to see something different ..=
. not to say more ;) ... but this is what i got.
> >=20
> >=20
> > regards, Thorsten
> >=20
> >=20
> >=20
> > Am 18.04.2012 um 15:50 schrieb Sascha Gottfried:
> >=20
> > > An additional note to my fist reply.
> > >=20
> > > I did setup a clean Zope 2.13.13/ZMS 2.13.4 using zc.buildout. I enab=
led verbose-security and the python security implementation and wrote a sma=
ll python script that should call a common ObjectManager method that REQUIR=
ES the user to have a special permission 'import_export_objects'. Before ru=
nning this sample you need to edit error_log in Zope Root Folder, remove 'U=
nauthorized' from 'ignored exception types' and enable 'Copy exceptions to =
the event log'. Usually this permission is assigned to role Manager. When r=
unning this script as anonymous user with a valid id pointing to an item in=
this folder the security policy is raising the exception below.
> > >=20
> > > -> export.py - in line number 8
> > > context.manage_exportObject(id=3Did, download=3DTrue)
> > >=20
> > > -> OFS.ObjectManager.py
> > >=20
> > > security.declareProtected(import_export_objects, 'manage_exportObject=
')
> > > def manage_exportObject(self, id=3D'', download=3DNone, toxml=3DNone,=
=20
> > > RESPONSE=3DNone,REQUEST=3DNone):
> > > """Exports an object to a file and returns that file."""
> > >=20
> > > -> event.log
> > >=20
> > > 2012-04-18 14:24:29 ERROR Zope.SiteErrorLog 1334751869.040.8339546883=
85 http://localhost:8080/sites/export
> > > Traceback (innermost last):
> > > Module ZPublisher.Publish, line 126, in publish
> > > Module ZPublisher.mapply, line 77, in mapply
> > > Module ZPublisher.Publish, line 46, in call_object
> > > Module Shared.DC.Scripts.Bindings, line 322, in __call__
> > > Module Shared.DC.Scripts.Bindings, line 359, in _bindAndExec
> > > Module Products.PythonScripts.PythonScript, line 344, in _exec
> > > Module script, line 8, in export
> > > - <PythonScript at /sites/export>
> > > - Line 8
> > > Module AccessControl.ImplPython, line 716, in guarded_getattr
> > > Module AccessControl.ImplPython, line 658, in aq_validate
> > > Module AccessControl.ImplPython, line 552, in validate
> > > Module AccessControl.ImplPython, line 450, in validate
> > > Module AccessControl.ImplPython, line 797, in raiseVerbose
> > > Unauthorized: Your user account does not have the required permission=
. Access to 'manage_exportObject' of (Folder at /sites) denied. Your user a=
ccount, Anonymous User, exists at=20
> > >=20
> > > /acl_users. Access requires Import_Export_objects_Permission, granted=
to the following roles: ['Manager']. Your roles in this context are ['Anon=
ymous'].
> > >=20
> > > This information really helps a lot. The line in my one-liner (export=
- line 8) is identified, you can see that the python security implementati=
on is handling the security check caused by the security attributes of meth=
od definition OFS.ObjectManager.manage_exportObject()
> > >=20
> > > The accessed method 'manage_exportObject' is mentioned and even the o=
bject that was used (Folder at /sites). The current user is specified, foll=
owed by the user folder. At last the security framework tells you details a=
bout the current permission/role mapping, even providing you with details a=
bout your current roles. I could not imagine a more useful message.
> > >=20
> > > Try this and provide a traceback as shown above. Then we do not guess=
anymore.
> > >=20
> > > Good luck.
> > > Sascha
> > >=20
> > > --- In [email protected], Thorsten Weber <tweber@> wrote=
:
> > > >
> > > > Hello again,
> > > >=20
> > > > I just proceeded as suggested by Sascha and activated verbose-secur=
ity ( zope.conf, line 622 )
> > > >=20
> > > > security-policy-implementation python
> > > > verbose-security on
> > > >=20
> > > > when calling the onChangeObjEvt i find this as Traceback in error_l=
og
> > > >=20
> > > >=20
> > > > Time 2012/04/12 16:07:42.074109 GMT+2=20
> > > > User Name (User Id) Anonymous User (None)=20
> > > > Request URL http://my_nackend_url/content/e94/manage_main=20
> > > > Exception Type Unauthorized
> > > > Exception Value <strong>You are not authorized to access this resou=
rce.</strong>
> > > > Traceback (innermost last):
> > > > Module ZPublisher.Publish, line 116, in publish
> > > > Module ZPublisher.BaseRequest, line 609, in traverse
> > > > Module ZPublisher.HTTPResponse, line 718, in unauthorized
> > > > Unauthorized: <strong>You are not authorized to access this resourc=
e.</strong>
> > > >=20
> > > > This does not say so much to me ... does this help in any way? ( or=
did i miss something? )
> > > >=20
> > > > but it is strange that the User is Anonymous !
> > > >=20
> > > >=20
> > > >=20
> > > > kind regards,=20
> > > > Thorsten Weber
> > > >=20
> > > >=20
> > > > Am 04.04.2012 um 09:51 schrieb Thorsten Weber:
> > > >=20
> > > > > Hello Sascha,
> > > > >=20
> > > > >=20
> > > > > thanks a lot for your detailed and very insightful explanation!
> > > > > and you are probably right concerning the exception - which might=
be raised silently.
> > > > >=20
> > > > > until the 10. of april I'm out of the office ...=20
> > > > > but i will come back to this as soon as possible and will apply v=
erbose security on a dev instance and provide the traceback.
> > > > >=20
> > > > >=20
> > > > > this for now:
> > > > >=20
> > > > > ZMS: ZMS2 2.12.1.1047 (Build #132e)
> > > > > ZOPE: (2.12.3, python 2.5.2, linux2)
> > > > > PYTHON: 2.5.2
> > > > >=20
> > > > > we generate some xml to feed a flash application when storing an =
object within zms and store it in local filesystem (for performance reasons=
)
> > > > > (localfs_write(path_to_xml, xml_structur_text, mode=3D'b'))
> > > > >=20
> > > > > and purge that object from cache afterwards
> > > > > (localfs_command('/usr/bin/squidclient -h my_squid_ipaddress -p 8=
0 -m PURGE http://my_frontend_url')
> > > > >=20
> > > > > all triggered by onChangeObjEvt
> > > > >=20
> > > > > but we were facing the problem that the method seems not have the=
right to execute the commands
> > > > > 1. write the new xml file
> > > > > 2. calling the squidclient
> > > > >=20
> > > > > Giving proxy manager rights to that method solved the problem so =
far.
> > > > >=20
> > > > >=20
> > > > > but from a web application developer view one may expect that the=
se "batteries" might be included within these zms-api calls
> > > > > if only when called by with a rather privileged account as ZMSAut=
hor ... no?
> > > > >=20
> > > > >=20
> > > > > kind regards,=20
> > > > > Thorsten Weber
> > > > >=20
> > > > >=20
> > > > >=20
> > > > > Am 03.04.2012 um 16:10 schrieb Sascha Gottfried:
> > > > >=20
> > > > >>=20
> > > > >> Hi Thorsten,
> > > > >> Zope Products (in your case: ZMS) offer an API that is available=
to restricted code if the required permission can be acquired in the reque=
st context.
> > > > >>=20
> > > > >> ZMS 2.12.1 (more specific: class ZMSGlobals.py) offered the meth=
od 'localfs_command' as a public method without requiring any permission fr=
om the user/context to call it. As of today this method is not available an=
ymore as a result of a former security review.
> > > > >>=20
> > > > >> That most probably means, that not the call of the method 'locaf=
s_command' is raising an exception, but the code in your method 'onChangeOb=
jEvt'. Please provide the traceback to your problem - without I am just gue=
ssing. I do not want to guess.
> > > > >>=20
> > > > >> Another hint - during development please change your zope instan=
ce to use 'Verbose Security'. Now a security exception tells you exactly wh=
at permission is required and more. This link shows how to change zope.conf=
(http://plone.org/documentation/kb/debug-unauthorized)
> > > > >>=20
> > > > >> If you don't mind provide some application details to your next =
answer. Please do not forget the traceback. And if you do not mind tell me =
more about the task you are trying to do.
> > > > >>=20
> > > > >> @Nils: I accidently addressed the last answer to you.
> > > > >>=20
> > > > >> A more secure way would be just to find out which permission is =
needed and to give this permission to a user. This applies to existing zope=
permissions currently not available to ZMSAuthor.
> > > > >>=20
> > > > >> Another way would be to register a new permission in your applic=
ation and add it to custom ZMS roles. In your script you could use Zope's A=
PI method checkPermission() or similar to verify that the caller has this p=
ermission. Now it is up to a zope administrator to provide this permission =
to certain users.=20
> > > > >>=20
> > > > >> If more methods in ZMS like 'localfs_command' had a proper permi=
ssion requirement, it would be more easy to application developers to provi=
de the permissions needed. Usual zope products contain code like this.
> > > > >>=20
> > > > >> security.declareProtected(permissionName, methodName)
> > > > >>=20
> > > > >> source:
> > > > >> http://docs.zope.org/zope2/zdgbook/Security.html#implementing-se=
curity-in-python-products
> > > > >>=20
> > > > >> Users need this permission to call this method from restriced co=
de (like any python script in ZODB). With this in mind - access control to =
application logic is a matter of user administration.
> > > > >>=20
> > > > >> That restriction does not apply to file system code like Zope pr=
oducts - but ZMS developers usually implement business logic in restricted =
code. That's why it is important to know the stuff mentioned above.
> > > > >>=20
> > > > >> Looking forward to see the traceback,
> > > > >> Sascha
> > > > >>=20
> > > > >> --- In [email protected], Niels Dettenbach <nd@> wr=
ote:
> > > > >> >
> > > > >> > Am Dienstag, 3. April 2012, 09:21:58 schrieben Sie:
> > > > >> > > what you did as well is giving a script all zope permissions=
available. This
> > > > >> > > script is available for anybody in the web. With this script=
you access
> > > > >> > > resources in your file system.=20
> > > > >> > ...shure it does - so it is very (!) important to know what th=
e script does=20
> > > > >> > and allows by theory and in practice...
> > > > >> >=20
> > > > >> > > Zope has a lot of secure answers for that stuff. If you want=
to know, why=20
> > > > >> > > this worked in ZMS a couple of months ago and what you reall=
y need to do,=20
> > > > >> > > ask me. But I am not sure, if you need a quick fix or a secu=
re solution for
> > > > >> > > your customers.=20
> > > > >> >=20
> > > > >> > Can you give some more details or examples? "a lot of " is rea=
lly nothing more=20
> > > > >> > then helpless to anyone here on the list...
> > > > >> >=20
> > > > >> >=20
> > > > >> > cheers,
> > > > >> >=20
> > > > >> >=20
> > > > >> > Niels.
> > > > >> > --=20
> > > > >> > ---
> > > > >> > Niels Dettenbach
> > > > >> > Syndicat IT & Internet
> > > > >> > http://www.syndicat.com
> > > > >> > ---
> > > > >> >
> > > > >>=20
> > > > >=20
> > > > >=20
> > > > >
> > > >
> > >=20
> > >
> >
>=20
>=20
--Apple-Mail=_96B2F2FC-99A9-48FA-A8DC-3B891D2BE209
Content-Type: text/html; charset=windows-1252
Content-Transfer-Encoding: 7bit
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
</head>
<body style="background-color: #fff;">
<span style="display:none"> </span>
<!--~-|**|PrettyHtmlStartT|**|-~-->
<div id="ygrp-mlmsg" style="position:relative;">
<div id="ygrp-msg" style="z-index: 1;">
<!--~-|**|PrettyHtmlEndT|**|-~-->
<div id="ygrp-text" >
<p>Hi Sascha,<div><br></div><div>1) FS_home.xml is a dtml-methode which generates a xml-file to tell a flash app to load external resources</div><div>in the backend there are various container object where links to special objects can be added</div><div>from these special objects details like text, images or videos are retrieved in a loop (<dtml-in "getObjChildren('tabs',REQUEST)">)</div><div>in order to minimize the render- and/or load time of the xml onChangeObjEvent is called with a pre-render param to tell FS_home.xml to be rendered into local file system. when FS_home.xml is called from the frontend, when it is loaded from squid => apache => file-system.</div><div><br></div><div>2) we use http_import to get the rendered source code of FS_home.xml. 
;</div><div>within the source code the substring of the backend url is striped out.</div><div>afterwards localfs_write with mode=b writes the codes into path + filename.</div><div><br></div><div>I dont use http_import often - only if it is necessary to process/parse the source code of a external resource.</div><div><br></div><div>3) yes. they are in the same scope but - as mentioned above - we are trying to reduce load time by this.</div><div><br></div><div>4) to port onChangeObjEvent as python script could be a way ... </div><div>but as we do not trigger security related stuff (like creating objects) it might be ok to set proxy role. </div><div>NO?</div><div><br></div><div>Or is it still a security risk?<br>
Maybe you can explain a bit more how such a script could be abused? </div><div>Is there - in theory - a way like XSS or Code injection?</div><div><br></div><div>regards, Thorsten</div><div><br></div><div><br></div><div><br></div><div><br><div><div>Am 19.04.2012 um 18:04 schrieb Sascha Gottfried:</div><br class="Apple-interchange-newline"><blockquote type="cite">
<div style="background-color: #fff;">
<span> </span>
<div id="ygrp-text"><p>Hi Thorsten,<br>
<br>
1) please add the source code for <a href="http://myObj_url/FS_home.xml">http://myObj_url/FS_home.xml</a><br>
Is it a more python script or DTML method?<br>
<br>
2) Explain your need for using http_import()! Did you often used this method before? Try to explain why it is not possible with plain DTML tags. Look at the sample code below<br>
<br>
onChangeObjEvt() explained: <br>
<a href="http://www.zms-publishing.com/dokumentation/literatur/professional_manual/api_tricks/onchangeobjevt/index_ger.html">http://www.zms-publishing.com/dokumentation/literatur/professional_manual/api_tricks/onchangeobjevt/index_ger.html</a><br>
<br>
3) Switch to a simple call of whatever FS_Home.xml is and pass parameters appropriately. This should be possible because FS_Home.xml and onChangeObjEvt() are in the scope of the same special object definition. Right?<br>
<br>
4)What about implementing onChangeObjEvt() in python? <br>
<br>
See you later!<br>
Sascha<br>
<br>
--- In <a href="mailto:zms-developers%40yahoogroups.com">[email protected]</a>, Thorsten Weber <tweber@...> wrote:<br>
><br>
> Hi Sascha,<br>
> <br>
> first of all - thank you so much for your efforts and explanations! <br>
> this really opens a door for me...<br>
> my first post was more a guess than knowledge ... <br>
> based on what the behaviors we observed (it worked for admin, not for ZMSAuthors).<br>
> <br>
> so I tried my best to provide a traceback as required ... but i am afraid i fail to do so.<br>
> <br>
> anyway ... <br>
> <br>
> i took away the proxy role manager from the methode myObj_meta_id.onChangeObjEvent<br>
> <br>
> this is what the methode looks like:<br>
> <br>
> <dtml-comment> -- // prerender FS_Home.xml from myObj // -- </dtml-comment><br>
> <dtml-let xml_text="http_import('<a href="http://myObj_url/FS_home.xml?prerender=true&rand=">http://myObj_url/FS_home.xml?prerender=true&rand=</a>'+ZopeTime().strftime('%Y%m%d%H%M%S'))"<br>
> path="'/srv/project/virtualenv/xml/'"><br>
> <br>
> <dtml-if xml_text><br>
> <dtml-comment> -- // write FS_Home.xml to disk // -- </dtml-comment><br>
> <dtml-call "localfs_write(path+'FS_home.xml',xml_text,mode='b')"><br>
> </dtml-if><br>
> <br>
> <dtml-comment> -- // remove FS_Home.xml from Squid cache // -- </dtml-comment><br>
> <dtml-call "localfs_command('/usr/bin/squidclient -h 85.25.124.231 -p 80 -m PURGE <a href="http://myObj_url/FS_home.xml">http://myObj_url/FS_home.xml</a>')"><br>
> <br>
> </dtml-let><br>
> <br>
> ZMS.localfs_read is granted for path.<br>
> <br>
> and called the methode with user credentials of ZMSAuthor (HTTP_Auth with IE8 Browser and with Firefox/MacOSX)<br>
> <br>
> and this is what i find in event_log:<br>
> <br>
> ------<br>
> 2012-04-19T15:11:29 ERROR Zope.SiteErrorLog 1334841089.320.269759942749 <a href="http://my_url/content/metaobj_manager/myObj_meta_id.onChangeObjEvt">http://my_url/content/metaobj_manager/myObj_meta_id.onChangeObjEvt</a><br>
> Traceback (innermost last):<br>
> Module ZPublisher.Publish, line 116, in publish<br>
> Module ZPublisher.BaseRequest, line 609, in traverse<br>
> Module ZPublisher.HTTPResponse, line 718, in unauthorized<br>
> Unauthorized: <strong>You are not authorized to access this resource.</strong><p><br>
> No Authorization header found.</p><br>
> ------<br>
> 2012-04-19T15:11:29 ERROR root Exception while rendering an error message<br>
> Traceback (most recent call last):<br>
> File "/srv/my_url/virtualenv/zope/lib/python2.5/site-packages/Zope2-2.12.3-py2.5-linux-x86_64.egg/OFS/SimpleItem.py", line 287, in raise_standardErrorMessage<br>
> v = s(client, REQUEST, **kwargs)<br>
> File "/srv/my_url/virtualenv/zope/lib/python2.5/site-packages/Zope2-2.12.3-py2.5-linux-x86_64.egg/OFS/DTMLMethod.py", line 149, in __call__<br>
> r = apply(HTML.__call__, (self, client, REQUEST), kw)<br>
> File "/srv/my_url/virtualenv/zope/lib/python2.5/site-packages/Zope2-2.12.3-py2.5-linux-x86_64.egg/DocumentTemplate/DT_String.py", line 477, in __call__<br>
> try: result = render_blocks(self._v_blocks, md)<br>
> File "/srv/my_url/virtualenv/zope/lib/python2.5/site-packages/Zope2-2.12.3-py2.5-linux-x86_64.egg/DocumentTemplate/DT_Util.py", line 202, in eval<br>
> return eval(code, d)<br>
> File "<string>", line 1, in <expression><br>
> File "/srv/my_url/virtualenv/zope/lib/python2.5/site-packages/Zope2-2.12.3-py2.5-linux-x86_64.egg/OFS/DTMLMethod.py", line 149, in __call__<br>
> r = apply(HTML.__call__, (self, client, REQUEST), kw)<br>
> File "/srv/my_url/virtualenv/zope/lib/python2.5/site-packages/Zope2-2.12.3-py2.5-linux-x86_64.egg/DocumentTemplate/DT_String.py", line 477, in __call__<br>
> try: result = render_blocks(self._v_blocks, md)<br>
> File "/srv/my_url/virtualenv/zope/lib/python2.5/site-packages/Zope2-2.12.3-py2.5-linux-x86_64.egg/OFS/DTMLMethod.py", line 140, in __call__<br>
> r = apply(HTML.__call__, (self, client, REQUEST), kw)<br>
> File "/srv/my_url/virtualenv/zope/lib/python2.5/site-packages/Zope2-2.12.3-py2.5-linux-x86_64.egg/DocumentTemplate/DT_String.py", line 477, in __call__<br>
> try: result = render_blocks(self._v_blocks, md)<br>
> File "/srv/my_url/virtualenv/zope/lib/python2.5/site-packages/Zope2-2.12.3-py2.5-linux-x86_64.egg/DocumentTemplate/DT_Util.py", line 202, in eval<br>
> return eval(code, d)<br>
> File "<string>", line 1, in <expression><br>
> NameError: name 'lang' is not defined<br>
> <br>
> <br>
> Me (and probably you) would have expected to see something different ... not to say more ;) ... but this is what i got.<br>
> <br>
> <br>
> regards, Thorsten<br>
> <br>
> <br>
> <br>
> Am 18.04.2012 um 15:50 schrieb Sascha Gottfried:<br>
> <br>
> > An additional note to my fist reply.<br>
> > <br>
> > I did setup a clean Zope 2.13.13/ZMS 2.13.4 using zc.buildout. I enabled verbose-security and the python security implementation and wrote a small python script that should call a common ObjectManager method that REQUIRES the user to have a special permission 'import_export_objects'. Before running this sample you need to edit error_log in Zope Root Folder, remove 'Unauthorized' from 'ignored exception types' and enable 'Copy exceptions to the event log'. Usually this permission is assigned to role Manager. When running this script as anonymous user with a valid id pointing to an item in this folder the security policy is raising the exception below.<br>
> > <br>
> > -> export.py - in line number 8<br>
> > context.manage_exportObject(id=id, download=True)<br>
> > <br>
> > -> OFS.ObjectManager.py<br>
> > <br>
> > security.declareProtected(import_export_objects, 'manage_exportObject')<br>
> > def manage_exportObject(self, id='', download=None, toxml=None, <br>
> > RESPONSE=None,REQUEST=None):<br>
> > """Exports an object to a file and returns that file."""<br>
> > <br>
> > -> event.log<br>
> > <br>
> > 2012-04-18 14:24:29 ERROR Zope.SiteErrorLog 1334751869.040.833954688385 <a href="http://localhost:8080/sites/export">http://localhost:8080/sites/export</a><br>
> > Traceback (innermost last):<br>
> > Module ZPublisher.Publish, line 126, in publish<br>
> > Module ZPublisher.mapply, line 77, in mapply<br>
> > Module ZPublisher.Publish, line 46, in call_object<br>
> > Module Shared.DC.Scripts.Bindings, line 322, in __call__<br>
> > Module Shared.DC.Scripts.Bindings, line 359, in _bindAndExec<br>
> > Module Products.PythonScripts.PythonScript, line 344, in _exec<br>
> > Module script, line 8, in export<br>
> > - <PythonScript at /sites/export><br>
> > - Line 8<br>
> > Module AccessControl.ImplPython, line 716, in guarded_getattr<br>
> > Module AccessControl.ImplPython, line 658, in aq_validate<br>
> > Module AccessControl.ImplPython, line 552, in validate<br>
> > Module AccessControl.ImplPython, line 450, in validate<br>
> > Module AccessControl.ImplPython, line 797, in raiseVerbose<br>
> > Unauthorized: Your user account does not have the required permission. Access to 'manage_exportObject' of (Folder at /sites) denied. Your user account, Anonymous User, exists at <br>
> > <br>
> > /acl_users. Access requires Import_Export_objects_Permission, granted to the following roles: ['Manager']. Your roles in this context are ['Anonymous'].<br>
> > <br>
> > This information really helps a lot. The line in my one-liner (export - line 8) is identified, you can see that the python security implementation is handling the security check caused by the security attributes of method definition OFS.ObjectManager.manage_exportObject()<br>
> > <br>
> > The accessed method 'manage_exportObject' is mentioned and even the object that was used (Folder at /sites). The current user is specified, followed by the user folder. At last the security framework tells you details about the current permission/role mapping, even providing you with details about your current roles. I could not imagine a more useful message.<br>
> > <br>
> > Try this and provide a traceback as shown above. Then we do not guess anymore.<br>
> > <br>
> > Good luck.<br>
> > Sascha<br>
> > <br>
> > --- In <a href="mailto:zms-developers%40yahoogroups.com">[email protected]</a>, Thorsten Weber <tweber@> wrote:<br>
> > ><br>
> > > Hello again,<br>
> > > <br>
> > > I just proceeded as suggested by Sascha and activated verbose-security ( zope.conf, line 622 )<br>
> > > <br>
> > > security-policy-implementation python<br>
> > > verbose-security on<br>
> > > <br>
> > > when calling the onChangeObjEvt i find this as Traceback in error_log<br>
> > > <br>
> > > <br>
> > > Time 2012/04/12 16:07:42.074109 GMT+2 <br>
> > > User Name (User Id) Anonymous User (None) <br>
> > > Request URL <a href="http://my_nackend_url/content/e94/manage_main">http://my_nackend_url/content/e94/manage_main</a> <br>
> > > Exception Type Unauthorized<br>
> > > Exception Value <strong>You are not authorized to access this resource.</strong><br>
> > > Traceback (innermost last):<br>
> > > Module ZPublisher.Publish, line 116, in publish<br>
> > > Module ZPublisher.BaseRequest, line 609, in traverse<br>
> > > Module ZPublisher.HTTPResponse, line 718, in unauthorized<br>
> > > Unauthorized: <strong>You are not authorized to access this resource.</strong><br>
> > > <br>
> > > This does not say so much to me ... does this help in any way? ( or did i miss something? )<br>
> > > <br>
> > > but it is strange that the User is Anonymous !<br>
> > > <br>
> > > <br>
> > > <br>
> > > kind regards, <br>
> > > Thorsten Weber<br>
> > > <br>
> > > <br>
> > > Am 04.04.2012 um 09:51 schrieb Thorsten Weber:<br>
> > > <br>
> > > > Hello Sascha,<br>
> > > > <br>
> > > > <br>
> > > > thanks a lot for your detailed and very insightful explanation!<br>
> > > > and you are probably right concerning the exception - which might be raised silently.<br>
> > > > <br>
> > > > until the 10. of april I'm out of the office ... <br>
> > > > but i will come back to this as soon as possible and will apply verbose security on a dev instance and provide the traceback.<br>
> > > > <br>
> > > > <br>
> > > > this for now:<br>
> > > > <br>
> > > > ZMS: ZMS2 2.12.1.1047 (Build #132e)<br>
> > > > ZOPE: (2.12.3, python 2.5.2, linux2)<br>
> > > > PYTHON: 2.5.2<br>
> > > > <br>
> > > > we generate some xml to feed a flash application when storing an object within zms and store it in local filesystem (for performance reasons)<br>
> > > > (localfs_write(path_to_xml, xml_structur_text, mode='b'))<br>
> > > > <br>
> > > > and purge that object from cache afterwards<br>
> > > > (localfs_command('/usr/bin/squidclient -h my_squid_ipaddress -p 80 -m PURGE <a href="http://my_frontend_url/">http://my_frontend_url</a>')<br>
> > > > <br>
> > > > all triggered by onChangeObjEvt<br>
> > > > <br>
> > > > but we were facing the problem that the method seems not have the right to execute the commands<br>
> > > > 1. write the new xml file<br>
> > > > 2. calling the squidclient<br>
> > > > <br>
> > > > Giving proxy manager rights to that method solved the problem so far.<br>
> > > > <br>
> > > > <br>
> > > > but from a web application developer view one may expect that these "batteries" might be included within these zms-api calls<br>
> > > > if only when called by with a rather privileged account as ZMSAuthor ... no?<br>
> > > > <br>
> > > > <br>
> > > > kind regards, <br>
> > > > Thorsten Weber<br>
> > > > <br>
> > > > <br>
> > > > <br>
> > > > Am 03.04.2012 um 16:10 schrieb Sascha Gottfried:<br>
> > > > <br>
> > > >> <br>
> > > >> Hi Thorsten,<br>
> > > >> Zope Products (in your case: ZMS) offer an API that is available to restricted code if the required permission can be acquired in the request context.<br>
> > > >> <br>
> > > >> ZMS 2.12.1 (more specific: class ZMSGlobals.py) offered the method 'localfs_command' as a public method without requiring any permission from the user/context to call it. As of today this method is not available anymore as a result of a former security review.<br>
> > > >> <br>
> > > >> That most probably means, that not the call of the method 'locafs_command' is raising an exception, but the code in your method 'onChangeObjEvt'. Please provide the traceback to your problem - without I am just guessing. I do not want to guess.<br>
> > > >> <br>
> > > >> Another hint - during development please change your zope instance to use 'Verbose Security'. Now a security exception tells you exactly what permission is required and more. This link shows how to change zope.conf (<a href="http://plone.org/documentation/kb/debug-unauthorized">http://plone.org/documentation/kb/debug-unauthorized</a>)<br>
> > > >> <br>
> > > >> If you don't mind provide some application details to your next answer. Please do not forget the traceback. And if you do not mind tell me more about the task you are trying to do.<br>
> > > >> <br>
> > > >> @Nils: I accidently addressed the last answer to you.<br>
> > > >> <br>
> > > >> A more secure way would be just to find out which permission is needed and to give this permission to a user. This applies to existing zope permissions currently not available to ZMSAuthor.<br>
> > > >> <br>
> > > >> Another way would be to register a new permission in your application and add it to custom ZMS roles. In your script you could use Zope's API method checkPermission() or similar to verify that the caller has this permission. Now it is up to a zope administrator to provide this permission to certain users. <br>
> > > >> <br>
> > > >> If more methods in ZMS like 'localfs_command' had a proper permission requirement, it would be more easy to application developers to provide the permissions needed. Usual zope products contain code like this.<br>
> > > >> <br>
> > > >> security.declareProtected(permissionName, methodName)<br>
> > > >> <br>
> > > >> source:<br>
> > > >> <a href="http://docs.zope.org/zope2/zdgbook/Security.html#implementing-security-in-python-products">http://docs.zope.org/zope2/zdgbook/Security.html#implementing-security-in-python-products</a><br>
> > > >> <br>
> > > >> Users need this permission to call this method from restriced code (like any python script in ZODB). With this in mind - access control to application logic is a matter of user administration.<br>
> > > >> <br>
> > > >> That restriction does not apply to file system code like Zope products - but ZMS developers usually implement business logic in restricted code. That's why it is important to know the stuff mentioned above.<br>
> > > >> <br>
> > > >> Looking forward to see the traceback,<br>
> > > >> Sascha<br>
> > > >> <br>
> > > >> --- In <a href="mailto:zms-developers%40yahoogroups.com">[email protected]</a>, Niels Dettenbach <nd@> wrote:<br>
> > > >> ><br>
> > > >> > Am Dienstag, 3. April 2012, 09:21:58 schrieben Sie:<br>
> > > >> > > what you did as well is giving a script all zope permissions available. This<br>
> > > >> > > script is available for anybody in the web. With this script you access<br>
> > > >> > > resources in your file system. <br>
> > > >> > ...shure it does - so it is very (!) important to know what the script does <br>
> > > >> > and allows by theory and in practice...<br>
> > > >> > <br>
> > > >> > > Zope has a lot of secure answers for that stuff. If you want to know, why <br>
> > > >> > > this worked in ZMS a couple of months ago and what you really need to do, <br>
> > > >> > > ask me. But I am not sure, if you need a quick fix or a secure solution for<br>
> > > >> > > your customers. <br>
> > > >> > <br>
> > > >> > Can you give some more details or examples? "a lot of " is really nothing more <br>
> > > >> > then helpless to anyone here on the list...<br>
> > > >> > <br>
> > > >> > <br>
> > > >> > cheers,<br>
> > > >> > <br>
> > > >> > <br>
> > > >> > Niels.<br>
> > > >> > -- <br>
> > > >> > ---<br>
> > > >> > Niels Dettenbach<br>
> > > >> > Syndicat IT & Internet<br>
> > > >> > <a href="http://www.syndicat.com/">http://www.syndicat.com</a><br>
> > > >> > ---<br>
> > > >> ><br>
> > > >> <br>
> > > > <br>
> > > > <br>
> > > ><br>
> > ><br>
> > <br>
> ><br>
><br>
<br>
</p>
</div>
</div>
<!-- end group email -->
</blockquote></div><br></div></p>
</div>
<!--~-|**|PrettyHtmlStart|**|-~-->
<div style="color: #fff; height: 0;">__._,_.___</div>
<div id="ygrp-actbar" style="clear: both; margin-bottom: 10px; white-space: nowrap; color: #666; padding-top: 15px;">
<div>
<a href="mailto:[email protected]?subject=Re%3A%20%5Bzms-developers%5D%20userrights%20for%20localfs_command%20-%20verbose%20security%20unauthorized%20exception" style="margin-right: 0; padding-right: 0;">
Reply to <span style="font-weight: 700;">sender</span></a> |
<a href="mailto:[email protected]?subject=Re%3A%20%5Bzms-developers%5D%20userrights%20for%20localfs_command%20-%20verbose%20security%20unauthorized%20exception">
Reply to <span style="font-weight: 700;">group</span></a> |
<a href="http://groups.yahoo.com/group/zms-developers/post;_ylc=X3oDMTJwMzl2MWZuBF9TAzk3MzU5NzE0BGdycElkAzg0NzA4MjEEZ3Jwc3BJZAMxNzA3MjgxOTQyBG1zZ0lkAzM3ODQEc2VjA2Z0cgRzbGsDcnBseQRzdGltZQMxMzM0OTA4MjIx?act=reply&messageNum=3784">Reply <span style="font-weight: 700;">via web post</span></a> |
<a href="http://groups.yahoo.com/group/zms-developers/post;_ylc=X3oDMTJlZDk4NGMwBF9TAzk3MzU5NzE0BGdycElkAzg0NzA4MjEEZ3Jwc3BJZAMxNzA3MjgxOTQyBHNlYwNmdHIEc2xrA250cGMEc3RpbWUDMTMzNDkwODIyMQ--" style="font-weight: 700;">Start a New Topic</a>
</div>
<a href="http://groups.yahoo.com/group/zms-developers/message/3770;_ylc=X3oDMTM0MmcxanJyBF9TAzk3MzU5NzE0BGdycElkAzg0NzA4MjEEZ3Jwc3BJZAMxNzA3MjgxOTQyBG1zZ0lkAzM3ODQEc2VjA2Z0cgRzbGsDdnRwYwRzdGltZQMxMzM0OTA4MjIxBHRwY0lkAzM3NzA-">Messages in this topic</a>
(<span style="font-weight: 700;">15</span>)
</div>
<!------- Start Nav Bar ------>
<!-- |**|begin egp html banner|**| -->
<div id="ygrp-vital" style="background-color: #e0ecee; font-family: Verdana; font-size: 10px; margin-bottom: 10px; padding: 10px;">
<span id="vithd" style="font-weight: bold; color: #333; text-transform: uppercase; ">Recent Activity:</span>
<ul style="list-style-type: none; margin: 0; padding: 0; display: inline;">
</ul>
<div style="clear: both; padding-top: 2px; color: #1e66ae;">
<a href="http://groups.yahoo.com/group/zms-developers;_ylc=X3oDMTJlcGJ0Y2ZtBF9TAzk3MzU5NzE0BGdycElkAzg0NzA4MjEEZ3Jwc3BJZAMxNzA3MjgxOTQyBHNlYwN2dGwEc2xrA3ZnaHAEc3RpbWUDMTMzNDkwODIyMQ--" style="text-decoration: none;">Visit Your Group</a>
</div>
</div>
<div id="ft" style="font-family: Arial; font-size: 11px; margin-top: 5px; padding: 0 2px 0 0; clear: both;">
<a href="http://groups.yahoo.com/;_ylc=X3oDMTJkbzhjYzMyBF9TAzk3NDc2NTkwBGdycElkAzg0NzA4MjEEZ3Jwc3BJZAMxNzA3MjgxOTQyBHNlYwNmdHIEc2xrA2dmcARzdGltZQMxMzM0OTA4MjIx" style="float: left;"><img src="http://l.yimg.com/a/i/us/yg/logo/us.gif" height="15" width="137" alt="Yahoo! Groups" style="border: 0;"/></a>
<div style="color: #747575; float: right;">Switch to: <a href="mailto:[email protected]?subject=Change Delivery Format: Traditional" style="text-decoration: none;">Text-Only</a>, <a href="mailto:[email protected]?subject=Email Delivery: Digest" class="margin-rt" style="text-decoration: none;">Daily Digest</a> • <a href="mailto:[email protected]?subject=Unsubscribe" style="text-decoration: none;">Unsubscribe</a> • <a href="http://docs.yahoo.com/info/terms/" style="text-decoration: none;">Terms of Use</a></div>
</div>
<!-- |**|end egp html banner|**| -->
</div> <!-- ygrp-msg -->
<!-- Sponsor -->
<!-- |**|begin egp html banner|**| -->
<div id="ygrp-sponsor" style="width:160px; float:right; clear:none; margin:0 0 25px 0; background: #fff;">
<!-- Start Recommendations -->
<div id="ygrp-reco">
</div>
<!-- End Recommendations -->
</div> <!-- |**|end egp html banner|**| -->
<div style="clear:both; color: #FFF; font-size:1px;">.</div>
</div>
<img src="http://geo.yahoo.com/serv?s=97359714/grpId=8470821/grpspId=1707281942/msgId=3784/stime=1334908221/nc1=3848641/nc2=4507179/nc3=5191953" width="1" height="1"> <br>
<div style="color: #fff; height: 0;">__,_._,___</div>
<!--~-|**|PrettyHtmlEnd|**|-~-->
</body>
<!--~-|**|PrettyHtmlStart|**|-~-->
<head>
<style type="text/css">
<!--
#ygrp-mkp {
border: 1px solid #d8d8d8;
font-family: Arial;
margin: 10px 0;
padding: 0 10px;
}
#ygrp-mkp hr {
border: 1px solid #d8d8d8;
}
#ygrp-mkp #hd {
color: #628c2a;
font-size: 85%;
font-weight: 700;
line-height: 122%;
margin: 10px 0;
}
#ygrp-mkp #ads {
margin-bottom: 10px;
}
#ygrp-mkp .ad {
padding: 0 0;
}
#ygrp-mkp .ad p {
margin: 0;
}
#ygrp-mkp .ad a {
color: #0000ff;
text-decoration: none;
}
#ygrp-sponsor #ygrp-lc {
font-family: Arial;
}
#ygrp-sponsor #ygrp-lc #hd {
margin: 10px 0px;
font-weight: 700;
font-size: 78%;
line-height: 122%;
}
#ygrp-sponsor #ygrp-lc .ad {
margin-bottom: 10px;
padding: 0 0;
}
a {
color: #1e66ae;
}
#actions {
font-family: Verdana;
font-size: 11px;
padding: 10px 0;
}
#activity {
background-color: #e0ecee;
float: left;
font-family: Verdana;
font-size: 10px;
padding: 10px;
}
#activity span {
font-weight: 700;
}
#activity span:first-child {
text-transform: uppercase;
}
#activity span a {
color: #5085b6;
text-decoration: none;
}
#activity span span {
color: #ff7900;
}
#activity span .underline {
text-decoration: underline;
}
.attach {
clear: both;
display: table;
font-family: Arial;
font-size: 12px;
padding: 10px 0;
width: 400px;
}
.attach div a {
text-decoration: none;
}
.attach img {
border: none;
padding-right: 5px;
}
.attach label {
display: block;
margin-bottom: 5px;
}
.attach label a {
text-decoration: none;
}
blockquote {
margin: 0 0 0 4px;
}
.bold {
font-family: Arial;
font-size: 13px;
font-weight: 700;
}
.bold a {
text-decoration: none;
}
dd.last p a {
font-family: Verdana;
font-weight: 700;
}
dd.last p span {
margin-right: 10px;
font-family: Verdana;
font-weight: 700;
}
dd.last p span.yshortcuts {
margin-right: 0;
}
div.attach-table div div a {
text-decoration: none;
}
div.attach-table {
width: 400px;
}
div.file-title a, div.file-title a:active, div.file-title a:hover, div.file-title a:visited {
text-decoration: none;
}
div.photo-title a, div.photo-title a:active, div.photo-title a:hover, div.photo-title a:visited {
text-decoration: none;
}
div#ygrp-mlmsg #ygrp-msg p a span.yshortcuts {
font-family: Verdana;
font-size: 10px;
font-weight: normal;
}
.green {
color: #628c2a;
}
.MsoNormal {
margin: 0 0 0 0;
}
o {
font-size: 0;
}
#photos div {
float: left;
width: 72px;
}
#photos div div {
border: 1px solid #666666;
height: 62px;
overflow: hidden;
width: 62px;
}
#photos div label {
color: #666666;
font-size: 10px;
overflow: hidden;
text-align: center;
white-space: nowrap;
width: 64px;
}
#reco-category {
font-size: 77%;
}
#reco-desc {
font-size: 77%;
}
.replbq {
margin: 4px;
}
#ygrp-actbar div a:first-child {
/* border-right: 0px solid #000;*/
margin-right: 2px;
padding-right: 5px;
}
#ygrp-mlmsg {
font-size: 13px;
font-family: Arial, helvetica,clean, sans-serif;
*font-size: small;
*font: x-small;
}
#ygrp-mlmsg table {
font-size: inherit;
font: 100%;
}
#ygrp-mlmsg select, input, textarea {
font: 99% Arial, Helvetica, clean, sans-serif;
}
#ygrp-mlmsg pre, code {
font:115% monospace;
*font-size:100%;
}
#ygrp-mlmsg * {
line-height: 1.22em;
}
#ygrp-mlmsg #logo {
padding-bottom: 10px;
}
#ygrp-mlmsg a {
color: #1E66AE;
}
#ygrp-msg p a {
font-family: Verdana;
}
#ygrp-msg p#attach-count span {
color: #1E66AE;
font-weight: 700;
}
#ygrp-reco #reco-head {
color: #ff7900;
font-weight: 700;
}
#ygrp-reco {
margin-bottom: 20px;
padding: 0px;
}
#ygrp-sponsor #ov li a {
font-size: 130%;
text-decoration: none;
}
#ygrp-sponsor #ov li {
font-size: 77%;
list-style-type: square;
padding: 6px 0;
}
#ygrp-sponsor #ov ul {
margin: 0;
padding: 0 0 0 8px;
}
#ygrp-text {
font-family: Georgia;
}
#ygrp-text p {
margin: 0 0 1em 0;
}
#ygrp-text tt {
font-size: 120%;
}
#ygrp-vital ul li:last-child {
border-right: none !important;
}
-->
</style>
</head>
<!--~-|**|PrettyHtmlEnd|**|-~-->
</html>
<!-- end group email -->
--Apple-Mail=_96B2F2FC-99A9-48FA-A8DC-3B891D2BE209--