From 44a91358ff01b8deca3416c26396069778a1bf42 Mon Sep 17 00:00:00 2001 From: Mario Date: Mon, 5 Aug 2024 00:01:31 +0200 Subject: [PATCH] Prevent URL manipulation in portal for vps --- ow_vm_management/__manifest__.py | 1 + .../__pycache__/portal.cpython-39.pyc | Bin 3258 -> 3639 bytes ow_vm_management/controllers/portal.py | 56 ++++++++++++------ .../__pycache__/vps_server.cpython-39.pyc | Bin 7203 -> 7559 bytes ow_vm_management/models/vps_server.py | 8 ++- ow_vm_management/security/ir.rule.xml | 15 +++++ 6 files changed, 60 insertions(+), 20 deletions(-) create mode 100644 ow_vm_management/security/ir.rule.xml diff --git a/ow_vm_management/__manifest__.py b/ow_vm_management/__manifest__.py index acdb619..e60fda0 100644 --- a/ow_vm_management/__manifest__.py +++ b/ow_vm_management/__manifest__.py @@ -16,6 +16,7 @@ ], 'data': [ 'security/ir.model.access.csv', + 'security/ir.rule.xml', 'views/vps_server_views.xml', 'views/res_partner_views.xml', 'views/portal_templates.xml', diff --git a/ow_vm_management/controllers/__pycache__/portal.cpython-39.pyc b/ow_vm_management/controllers/__pycache__/portal.cpython-39.pyc index 2f1acb11ba07680795d73d9e5f042e5620e2fd73..b17e14a7a71db759563f00d523656ff5d920aa4f 100644 GIT binary patch literal 3639 zcmai1TW=&s6|Snj_gw7tWn(X!ix3G6WNj`&39wlfasz>!B}N41rD%86*fZ{)o>ceP znNiORYbm@ezraXiiI+%7{GWP4;wk?iF(~4k>b5<0Oz7D@K6N{N>YVRfHfps(0?*$+ z`$zAu%Y^(32aAslgU9gZzXu|Spb1H7m!_=CD2`cTrFPdgyp=eq+jR|ZCtm7z{WR!? zX6z)5wApP^@-7js@J@*Er2o|FwuHY+I>DzPophMC4~k->Deq9-_*opwJbzoMOu^7o z^24#r3)s|tbDS4hD%F!r6;Yy{QM4~{4}0(Z7zHgpZav8V42&dQDoB@oOnOvM!A|_H zCGCMDEMf06;h<{5e`bs4L_;)B=u_Ht&3bE}F08i2(g}ezPi%-~ap#0~eY0!j+OAcx zW_AT)Q>=@-uq*tS*reNl3*Xbh@1E>d`p0ZRUk7-6EqL>{fE45@nbVS<(HSe)z#4$M zGYdIEH_6NvtfWUoJudCJi`OWvf!DM4VSmX4dq_$KpL2H3!=q8Yl}mLbm0kv(S3Hli z@vzX1xHc*8i*x!$$Jb6i9>LbH@sX0FNJ)MG7Uv^laefpf;0uL{YD*4}v^@rKI)shI zP%03jo4Jfsd{E10ebI5Woy(-Bee+4Fa_v@fv{#=qM%gtwenNG?d7MOf&iN`Z&!+LjV5Xdlnl_CO(29eX$U`` zZqVt=moC`)id&v9ZKsoMu-JpjV)^vN8xz}zzwL+pVew<+9w~IFx&!359ZTEAWF)m6 zjf(^A9m~DEU&v1>Ebhc;zl#Ue#8CVdy!k(Ylw?lM$jmM1z_@+pA!nTK3nr}68#w6b zSx~sberoV}%7FKP_l0}PL~zPxVQJ@2gfp{C;5LZ}0W^)0sj{@-4G`xg>sj!(RazjA zy+&q1>Ce3~0LV29U$jeBTBR+PPOY+8(vl1UvHZ-LwMu^wmOtLl2f`z@pKGVcFsaz+N&Dewt^<_A@k+{r1Koy@y=a^i9KX6!1<78dII7bh?&s!(`M)r91t8<$PD9`2B8$hEM0tE5<3k>q+N-eoS6SXyQuLLK ziz-|Vgs#I%zqEi%zAp9D^Ik{l<3cG0bV-s5BN?1)CcnSi8+7 zSQ~THfG|dvA+0a7H9*1&oxbqZzKjw17Y}A&>=2BzFr}cI1cSV3gQ*E*I|kVc%4*L6 zlUrzVQ%=I3v|Oz(1V)v4(%W{{sF3&ULK1GQ0_& z>@*a*dI5ImvzsaHZJ5*dqPXa1LtbBn_f(eF9gA)oKvvxgWL|^pj$Ng&cTwPDB$!if zrl6G?eHT)}VAu;^Vy{Wp0DZo};Pn2LAHIAnMtfd7h&r?_-q8pw$N;dnN#^W~%&ih^ z2pF{IHqH;6Gm7&wXSz{3(C9&XM+tCV#X(Y81FQOz_baRZ36jZ>K{9c`nruhgP#i@k zFrjQr2ZaryO}l$4JIQ$B=O2!v1nV;Arbjegn|2fwPpN(mLewvjV7#gKkRS|o zt8PkTnIv%VDpTFVEm*S@Hk(+`axAt!Yx;nX@2}9X8G1lNv%b@5YdaHJR`rUQlJpXe zx8z5$93hNzV_hAXS^RI2@m}|3Gp<_Tx+&ONXngA_h48A%>Q8{{u9{O>egyKNq0e*p hfI|2gZ5FO!E@Vot|FAOFBkWak?9?#JLY{>R`(MHSz1aW& literal 3258 zcmai0&2JmW6`z^?y=w4}yOg1U&~fNd={DcYokEYvOPrftd^bNi2J@fSlaFKnD@%C8z#BgWh_|wYRE8(D!C3o08>riFthU`DWh7@4dLy3J81; z|M|CmZ;g@_=4CZ3gFS-&^In${SS{GCv4@m7Vr)<{4aKal;jyXqZM7y zITP@IMASzm8(MIUaY8W#oUUy}2Wy4k4g%BQ7@ z@5MsoqoSK1^P`Ms@d)gjNtjLYQK|AYg=4z)rFO^!aZf8amwb6$}T<;&fKnmyG29*rvw#KPb&+WkDgqyz6JlS%7XLQhh*+o-kDeV z;8xAj7i$%(tjZSar&iUhXhnt%vGLrQhm|)BD!&SFqzc6iIKBPcp0_IZtOMbZGB|QtpR^=|x0k?Z9gg2@d5`4P0dvOM88UckscG7rXLj2}RNHq(7_2~8k zArwG>4hCZmPJn!esdsSyT_o?~AgJQ$3$Z+JNp z+AY9=_op34TN9OP9|(`tL7|;8M*y+Wpx~#a9BVi4_X}BetXh_n4zfY1-bc9@I?G_{ zc-qF}b@Sy|p&NJ($Hysj#i%J{Kc1u|N2%>PSoUMZ@j3u)h59b6n%%wX<~z%H%cgwg z^iE5^{lbBlE7EwHPs-K6R5##A8cxhH}2zhQ);MRB4 z?qqCU=PFPiK?#v{5&I!L~Q1aGg|lVkM;G7SK;#|CM%EAlKJjMRrP9tHSXZcMD2 zxD8dKeu(7F7vwGA^wzcSUWcjTArQiR8n8_(q~X`DV^f=kF!F{KSd20nKx5SYx7)C5 zj=KMGy_&}v^ZS?In7#KZE?g^j_SO;r>X=7M48&d+T(Sg&y9C>qHt|&^s(Zl9zE_eV zWEcQxwjgJg0dT9b&g=z&7$Ij4@a}>v=x5|HWQWq5eoSGeo)c_AUGhhm`936Fh$E|x zqjL*x)bR}f8#{x`PDHNVeU%>vJ8uT*<57Wu~cK)_WywU_$3kOS);z=i^2oiIi>0f09EhG%zPtLl}lvv;ln zt-)9$yiQ!8_5@w{;==@lcyHAGDKN7(!YJm)iY=_U{TUhE2Bp%H_1W};rD8$(tBM6k zb1EBvkqt;?{gwWO&YcpBBCN_W;AJnt>t`t7z1PL)$~7^902j8o$dd`CE1n$6W zRb%w5Fc;MJLyy+CR|1?HyC8rZ4J0JKI@#WbWgWM+PHRONWY@4hn3wqzIjT*zzOn*c z!$js)^KD+|?!N{(FAUfQ^KGzAI=lN?175!5eyQQ4LT=iBS;re zx%8b2&6`?9{Q%Yi*#8Lr3?FS@5>oKP0hE1e`VcC;t!uT_acZ2xlN8F=?vrB#`s?(d z@X6>sb=d%Z%f5E)*$1ya`TrRSqUYOiPOsPNFlbHfbCU!(*8%5QE+(l2KIHuKNt|MO z;M^1~ri9Rr0t}Z5Lr8s$D CK6dc{ diff --git a/ow_vm_management/controllers/portal.py b/ow_vm_management/controllers/portal.py index bdefd2a..e477ab1 100644 --- a/ow_vm_management/controllers/portal.py +++ b/ow_vm_management/controllers/portal.py @@ -1,27 +1,30 @@ from odoo import http, _ -from odoo.exceptions import AccessError, MissingError +from odoo.exceptions import AccessError from odoo.http import request from odoo.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager from odoo.osv.expression import OR -class CustomerPortalVPS(CustomerPortal): +class VPSPortal(CustomerPortal): def _prepare_home_portal_values(self, counters): values = super()._prepare_home_portal_values(counters) + partner = request.env.user.partner_id + VPSServer = request.env['vps.server'] if 'vps_server_count' in counters: - values['vps_server_count'] = request.env['vps.server'].search_count([('customer_id', '=', request.env.user.partner_id.id)]) + values['vps_server_count'] = VPSServer.search_count([('customer_id', '=', partner.id)]) return values @http.route(['/my/vps-servers', '/my/vps-servers/page/'], type='http', auth="user", website=True) def portal_my_vps_servers(self, page=1, date_begin=None, date_end=None, sortby=None, **kw): values = self._prepare_portal_layout_values() + partner = request.env.user.partner_id VPSServer = request.env['vps.server'] - domain = [('customer_id', '=', request.env.user.partner_id.id)] + domain = [('customer_id', '=', partner.id)] searchbar_sortings = { 'name': {'label': _('Name'), 'order': 'name'}, - 'ip_address': {'label': _('IP Address'), 'order': 'ip_address'}, + 'state': {'label': _('State'), 'order': 'state'}, } if not sortby: @@ -62,28 +65,43 @@ class CustomerPortalVPS(CustomerPortal): }) return request.render("ow_vm_management.portal_my_vps_servers", values) - @http.route(['/my/vps//restart'], type='http', auth="user", website=True) - def portal_restart_vps(self, vps_id, **kw): + @http.route(['/my/vps-servers/'], type='http', auth="user", website=True) + def portal_my_vps_server(self, vps_id, **kw): try: - vps_sudo = request.env['vps.server'].sudo().browse(vps_id) - vps_sudo.action_restart_from_portal() - return request.redirect(vps_sudo.get_portal_url()) + vps_sudo = self._document_check_access('vps.server', vps_id) except AccessError: return request.redirect('/my') - - @http.route(['/my/vps-servers/'], type='http', auth="user", website=True) - def portal_my_vps_server(self, vps_server_id=None, access_token=None, **kw): - try: - vps_server_sudo = self._document_check_access('vps.server', vps_server_id, access_token) - except (AccessError, MissingError): + + if vps_sudo.customer_id.id != request.env.user.partner_id.id: return request.redirect('/my') - values = self._vps_server_get_page_view_values(vps_server_sudo, access_token, **kw) + values = self._vps_server_get_page_view_values(vps_sudo, **kw) return request.render("ow_vm_management.portal_vps_server_page", values) - def _vps_server_get_page_view_values(self, vps_server, access_token, **kwargs): + def _vps_server_get_page_view_values(self, vps_server, access_token=None, **kwargs): values = { 'page_name': 'vps_server', 'vps_server': vps_server, } - return self._get_page_view_values(vps_server, access_token, values, 'my_vps_servers_history', False, **kwargs) \ No newline at end of file + return self._get_page_view_values(vps_server, access_token, values, 'my_vps_servers_history', False, **kwargs) + + @http.route(['/my/vps//restart'], type='http', auth="user", website=True) + def portal_restart_vps(self, vps_id, **kw): + try: + vps_sudo = self._document_check_access('vps.server', vps_id) + if vps_sudo.customer_id.id != request.env.user.partner_id.id: + return request.redirect('/my') + vps_sudo.action_restart_from_portal() + return request.redirect(vps_sudo.get_portal_url()) + except AccessError: + return request.redirect('/my') + + def _document_check_access(self, model_name, document_id, access_token=None): + document = request.env[model_name].sudo().browse(document_id) + document_sudo = document.with_user(request.env.user).sudo() + try: + document_sudo.check_access_rights('read') + document_sudo.check_access_rule('read') + except AccessError: + raise + return document_sudo \ No newline at end of file diff --git a/ow_vm_management/models/__pycache__/vps_server.cpython-39.pyc b/ow_vm_management/models/__pycache__/vps_server.cpython-39.pyc index 6748c755e4a8bb3e0f1062f6adeaefd037819bd2..5a8c53b597cea0c90fcf89ed1ca5a2df539aef42 100644 GIT binary patch delta 2065 zcmZ`(U1%It6rQ^?yF2^aq?@GqsqHq+-*#=1raz`X#zfo3N>d|gYhA@*GINvNWHYmz zxs$dr5^SQWf=GJBH?bmE17d~xDEQD95q$7qt84kr{<&x&N)%hM)v{jDqvRX*4Ep|18g4h+0!Z`pJg2aerQO{dj&yvd8FMpBe_)nyNd{vnucHVw= zMK2t*58J&fN}2<13f*#a)IKJ(d!&5aJ|X1Xs-h`{Lla7F zkRJ_w8i@(_A;EtQJ!;w|#GDE^6GUw~W%^|%mL95qkOqEF`@ZWL)ZK>=K?n<2C$Irjs)oq0-%$w%XXkhCVyPoqzKf}bCQ}E#xfV)iY1&!ZJF0GlxpIDDTcHBVybs3 zB*cveW%cRjVQ?3f8D)mt4cX*N0t1W04$Z1wmzY(nD>K@x3Ii<6BDT6rmdP!`qD)^@ zmP2=Ai^4{fqjqVbe$w=4?=-l6#i+Qz%yOUsHSLAm8}5>ywOyy1WsBxK&6=`5vdqo0 zV#&*19iPaG=9#C^n~Mi3b>;)j7ZX9OXnKYu3=)>_F?WuFQL=-0#RTR^8KS~}S<8?? zNf!{*TSaQk8r5wK@XK@}7m^VRVy-AUla*WtT2Nm`YcEPy1iU3a4=EIqCe0*C7TSb! z6II`~lkyv>_Xc}}E{r3*hHx2S0%6OqS8%u=p(Zu_LV7=$d{9b1R$H;Op&j8MLI*;% za=5#@Ept(f&kO&%ab7m#N&fln`{V#0%bX%7`0Y%0OAWKo3Um=5j~b=Q7yReUBzc?X z8wN=ow;Mhq4*$1dq*c0|L&D9w;Ay4tHyiWyFn}!TG-YrMmAd$y#(rY*hm8aIvp8`a z;e>!(OSKYD;lybK>0z}ddA4boT;pcbVM6&QO&!GMKQ#5!J&$TN9`nad`$+LYVoy7{ zww1()C5ng9pngu=rRFp>Jb%)&pfpLV0dB=U_z2Go9O#wU{h0m8=lL5nJ zA7K$^^)b`AIpB&8ktddU-n>VG*aR(8OSZrzG-*K4q@C+nMbi=NSutD!yu)D>;bnvi z2(p@G4Pe8->j<|51mQW?re(lr1BMCkI6=xVtg`8O7A=K2NY4n>&}{_u7t(qk*ha=tap85QN912gp~mkt{UaDA3Oc U7sX{TM8wF83uq>FM8`MuAB^SI-~a#s delta 1676 zcmZ`)O>7%g5cadZ_S&%>Ah8|CDKV+zWN}mHr$wQJG@(t>szekLKypFG-F<0oHoNBS z+D(ImD`JQ$PUclea03LQmli1}P%lV`8xjXjiz-FpfD|DP2rgV;=D9dl2wwTyc{AUe znKv`f-hS%S=}0jg4!Yp8`1{?pPtwiE0C}OA9X>`}noINO{=8qj<-9E3fqY=erFk`} z;nE~Z%E2v9zN>j7<0nqwMT@fL*D0bkfBJ)*6t ze{H>BU$4+sf*tvAEAT~h7AWjA!W_apLW++EW6@_&Jd4n|=Th*_%%OXLxEb2d(X6n; z9V~Qxet6LLL%shKQwiOxw_`eoT?U#xzAY1hDS(G}?jV)zocqx3G??vhtM_}oAs8MsgRhU61Sb?4xlod*v)1_7| z+gyLJm{(P5SqqGrOw0v4kDeYq;GTcnJxE^J`KJ3<&$$%Z9zz&HIEv6d{bn>*tdu5m zWy>^jhN)32SEtQ*bqQdq)x-#{rfjL(Ud0lqw(c zRP-{*^R4JK>E`#MACNL%iOmnKl#DAnicL2|gQ5kjp!2 z-w^RVf&@Q*FEKdvJc_*la7l6Q9dE6uTaJ%W)npp$L$h#|MComSANl=6Wl8M20ZSN; zmOGFg=^889lx?BD<&QN`?}00N(AT?Y;J5nAq|X2DPtLS6>G0>P8hXii9hxKVb2t4YrJ1dJw!~pJ}L9iw1Zfgk_!KFA7^kSVB05AO=@7 zb_JJj0XWh+jG!(`%CHs;PvV3N1ywIvmP&QKQ0VBQ=!9s3A9(n+<^W{kg-3QjL?Yth z-w$LSmVGkwl86-YF(fJ>?mIGZVFE-phHweMk(Nb#By@yn_u7$XAi=6mX9+MY{MYFt JAtK}B{s#!ya2x;t diff --git a/ow_vm_management/models/vps_server.py b/ow_vm_management/models/vps_server.py index 1482e8e..c60423d 100644 --- a/ow_vm_management/models/vps_server.py +++ b/ow_vm_management/models/vps_server.py @@ -1,5 +1,5 @@ from odoo import models, fields, api, _ -from odoo.exceptions import UserError +from odoo.exceptions import UserError, AccessError import requests import urllib3 import ipaddress @@ -89,6 +89,12 @@ class VPSServer(models.Model): status = self._proxmox_request('GET', f'nodes/pve/{vm_type}/{vm_id}/status/current') server.state = 'running' if status['data']['status'] == 'running' else 'stopped' + def check_access_rule(self, operation): + if self.env.user.has_group('base.group_portal'): + if operation != 'read' or self.customer_id != self.env.user.partner_id: + raise AccessError(_("You don't have access to this VPS server.")) + return super(VPSServer, self).check_access_rule(operation) + def action_restart_from_portal(self): self.ensure_one() if self.env.user.partner_id != self.customer_id: diff --git a/ow_vm_management/security/ir.rule.xml b/ow_vm_management/security/ir.rule.xml new file mode 100644 index 0000000..30923a3 --- /dev/null +++ b/ow_vm_management/security/ir.rule.xml @@ -0,0 +1,15 @@ + + + + + Portal user can only see own VPS servers + + [('customer_id', '=', user.partner_id.id)] + + + + + + + + \ No newline at end of file