nordabiz/docs/architecture/diagrams/a5-auth-oauth.drawio

183 lines
20 KiB
Plaintext

<mxfile host="draw.io" modified="2026-02-12" agent="Claude Code" type="device">
<diagram id="a5-auth" name="Autoryzacja i OAuth">
<mxGraphModel dx="1400" dy="900" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1600" pageHeight="1000" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="title" value="NordaBiz — Autoryzacja, Role i OAuth 2.0" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontSize=22;fontStyle=1;fontColor=#1a1a2e;" vertex="1" parent="1">
<mxGeometry x="400" y="15" width="530" height="36" as="geometry"/>
</mxCell>
<mxCell id="subtitle" value="Hierarchia ról systemowych, uprawnienia firmowe i integracja OAuth z Google/Meta" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontSize=11;fontColor=#888;" vertex="1" parent="1">
<mxGeometry x="380" y="48" width="560" height="20" as="geometry"/>
</mxCell>
<!-- ROLE SYSTEMOWE (lewa strona) -->
<mxCell id="roles_bg" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#E3F2FD;strokeColor=#1565C0;shadow=1;" vertex="1" parent="1">
<mxGeometry x="40" y="85" width="440" height="380" as="geometry"/>
</mxCell>
<mxCell id="roles_title" value="&lt;b style=&quot;font-size:16px;color:#1565C0&quot;&gt;Role Systemowe (SystemRole)&lt;/b&gt;" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="55" y="91" width="300" height="26" as="geometry"/>
</mxCell>
<!-- Piramida ról -->
<mxCell id="r_admin" value="&lt;b style=&quot;font-size:13px&quot;&gt;ADMIN (100)&lt;/b&gt;&lt;br&gt;&lt;font style=&quot;font-size:9px&quot;&gt;Pełna kontrola systemu&lt;br&gt;Zarządzanie użytkownikami&lt;/font&gt;" style="trapezoid;whiteSpace=wrap;html=1;fillColor=#C62828;strokeColor=#C62828;fontColor=#fff;fontSize=11;perimeter=trapezoidPerimeter;fixedSize=1;size=20;" vertex="1" parent="1">
<mxGeometry x="160" y="125" width="200" height="50" as="geometry"/>
</mxCell>
<mxCell id="r_office" value="&lt;b style=&quot;font-size:12px&quot;&gt;OFFICE_MANAGER (50)&lt;/b&gt;&lt;br&gt;&lt;font style=&quot;font-size:9px&quot;&gt;Panel admina | Moderacja forum | Edycja firm&lt;/font&gt;" style="trapezoid;whiteSpace=wrap;html=1;fillColor=#E65100;strokeColor=#E65100;fontColor=#fff;fontSize=11;perimeter=trapezoidPerimeter;fixedSize=1;size=15;" vertex="1" parent="1">
<mxGeometry x="130" y="180" width="260" height="45" as="geometry"/>
</mxCell>
<mxCell id="r_manager" value="&lt;b style=&quot;font-size:12px&quot;&gt;MANAGER (40)&lt;/b&gt;&lt;br&gt;&lt;font style=&quot;font-size:9px&quot;&gt;Pełna kontrola firmy | Zarządzanie pracownikami | Delegowanie uprawnień&lt;/font&gt;" style="trapezoid;whiteSpace=wrap;html=1;fillColor=#F9A825;strokeColor=#F9A825;fontColor=#fff;fontSize=11;perimeter=trapezoidPerimeter;fixedSize=1;size=12;" vertex="1" parent="1">
<mxGeometry x="105" y="230" width="310" height="45" as="geometry"/>
</mxCell>
<mxCell id="r_employee" value="&lt;b style=&quot;font-size:12px&quot;&gt;EMPLOYEE (30)&lt;/b&gt;&lt;br&gt;&lt;font style=&quot;font-size:9px&quot;&gt;Edycja danych firmy (delegowane uprawnienia) | Ogłoszenia | Forum&lt;/font&gt;" style="trapezoid;whiteSpace=wrap;html=1;fillColor=#2E7D32;strokeColor=#2E7D32;fontColor=#fff;fontSize=11;perimeter=trapezoidPerimeter;fixedSize=1;size=10;" vertex="1" parent="1">
<mxGeometry x="80" y="280" width="360" height="45" as="geometry"/>
</mxCell>
<mxCell id="r_member" value="&lt;b style=&quot;font-size:12px&quot;&gt;MEMBER (20)&lt;/b&gt;&lt;br&gt;&lt;font style=&quot;font-size:9px&quot;&gt;Kontakty firm | Forum | Chat AI | Ogłoszenia | Wiadomości&lt;/font&gt;" style="trapezoid;whiteSpace=wrap;html=1;fillColor=#1565C0;strokeColor=#1565C0;fontColor=#fff;fontSize=11;perimeter=trapezoidPerimeter;fixedSize=1;size=8;" vertex="1" parent="1">
<mxGeometry x="60" y="330" width="400" height="45" as="geometry"/>
</mxCell>
<mxCell id="r_unaffiliated" value="&lt;b style=&quot;font-size:12px&quot;&gt;UNAFFILIATED (10)&lt;/b&gt;&lt;br&gt;&lt;font style=&quot;font-size:9px&quot;&gt;Tylko profile publiczne | Ograniczony chat AI | Brak kontaktów&lt;/font&gt;" style="trapezoid;whiteSpace=wrap;html=1;fillColor=#9E9E9E;strokeColor=#9E9E9E;fontColor=#fff;fontSize=11;perimeter=trapezoidPerimeter;fixedSize=1;size=5;" vertex="1" parent="1">
<mxGeometry x="50" y="380" width="420" height="45" as="geometry"/>
</mxCell>
<mxCell id="roles_arrow" value="&lt;font style=&quot;font-size:10px;color:#1565C0&quot;&gt;↑ has_role() = hierarchiczne&lt;br&gt;Wyższa rola = wszystkie&lt;br&gt;uprawnienia niższych&lt;/font&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="60" y="430" width="180" height="28" as="geometry"/>
</mxCell>
<!-- UPRAWNIENIA FIRMOWE (prawy-góra) -->
<mxCell id="comp_bg" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#E8F5E9;strokeColor=#2E7D32;shadow=1;" vertex="1" parent="1">
<mxGeometry x="530" y="85" width="380" height="200" as="geometry"/>
</mxCell>
<mxCell id="comp_title" value="&lt;b style=&quot;font-size:14px;color:#2E7D32&quot;&gt;Role Firmowe (CompanyRole)&lt;/b&gt;" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="545" y="91" width="280" height="26" as="geometry"/>
</mxCell>
<mxCell id="cr1" value="&lt;b&gt;MANAGER (30)&lt;/b&gt; — pełna kontrola firmy + pracownicy" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff;strokeColor=#2E7D32;fontSize=10;align=left;spacingLeft=6;" vertex="1" parent="1">
<mxGeometry x="545" y="122" width="350" height="26" as="geometry"/>
</mxCell>
<mxCell id="cr2" value="&lt;b&gt;EMPLOYEE (20)&lt;/b&gt; — edycja danych (wg delegacji)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff;strokeColor=#2E7D32;fontSize=10;align=left;spacingLeft=6;" vertex="1" parent="1">
<mxGeometry x="545" y="154" width="350" height="26" as="geometry"/>
</mxCell>
<mxCell id="cr3" value="&lt;b&gt;VIEWER (10)&lt;/b&gt; — tylko odczyt dashboardu firmy" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff;strokeColor=#2E7D32;fontSize=10;align=left;spacingLeft=6;" vertex="1" parent="1">
<mxGeometry x="545" y="186" width="350" height="26" as="geometry"/>
</mxCell>
<mxCell id="comp_note" value="&lt;font style=&quot;font-size:10px;color:#2E7D32&quot;&gt;&lt;b&gt;Multi-company:&lt;/b&gt; user_companies (M:M)&lt;br&gt;Użytkownik może mieć różne role w różnych firmach&lt;/font&gt;" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="545" y="220" width="350" height="28" as="geometry"/>
</mxCell>
<!-- DELEGOWANE UPRAWNIENIA -->
<mxCell id="deleg_bg" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#FFF3E0;strokeColor=#E65100;shadow=1;" vertex="1" parent="1">
<mxGeometry x="530" y="300" width="380" height="165" as="geometry"/>
</mxCell>
<mxCell id="deleg_title" value="&lt;b style=&quot;font-size:14px;color:#E65100&quot;&gt;Delegowane Uprawnienia&lt;/b&gt;&lt;br&gt;&lt;font style=&quot;font-size:10px&quot;&gt;MANAGER → EMPLOYEE (user_company_permissions)&lt;/font&gt;" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="545" y="306" width="350" height="36" as="geometry"/>
</mxCell>
<mxCell id="dp1" value="can_edit_description — opis, historia, wartości" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff;strokeColor=#E65100;fontSize=10;align=left;spacingLeft=6;" vertex="1" parent="1">
<mxGeometry x="545" y="346" width="350" height="22" as="geometry"/>
</mxCell>
<mxCell id="dp2" value="can_edit_services — usługi, kompetencje, technologie" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff;strokeColor=#E65100;fontSize=10;align=left;spacingLeft=6;" vertex="1" parent="1">
<mxGeometry x="545" y="372" width="350" height="22" as="geometry"/>
</mxCell>
<mxCell id="dp3" value="can_edit_contacts — email, telefon, adres" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff;strokeColor=#E65100;fontSize=10;align=left;spacingLeft=6;" vertex="1" parent="1">
<mxGeometry x="545" y="398" width="350" height="22" as="geometry"/>
</mxCell>
<mxCell id="dp4" value="can_edit_social | can_manage_classifieds | can_post_forum | can_view_analytics" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff;strokeColor=#E65100;fontSize=10;align=left;spacingLeft=6;" vertex="1" parent="1">
<mxGeometry x="545" y="424" width="350" height="28" as="geometry"/>
</mxCell>
<!-- OAUTH 2.0 FLOW (dół) -->
<mxCell id="oauth_bg" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#F3E5F5;strokeColor=#7B1FA2;shadow=1;" vertex="1" parent="1">
<mxGeometry x="40" y="510" width="870" height="220" as="geometry"/>
</mxCell>
<mxCell id="oauth_title" value="&lt;b style=&quot;font-size:16px;color:#7B1FA2&quot;&gt;OAuth 2.0 — Integracja z API Zewnętrznymi&lt;/b&gt;" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="55" y="516" width="400" height="26" as="geometry"/>
</mxCell>
<!-- OAuth flow steps -->
<mxCell id="of1" value="&lt;b&gt;1. Initiate&lt;/b&gt;&lt;br&gt;&lt;font style=&quot;font-size:9px&quot;&gt;POST /api/oauth/&lt;br&gt;connect/{provider}&lt;br&gt;CSRF state token&lt;/font&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#CE93D8;strokeColor=#7B1FA2;fontSize=10;" vertex="1" parent="1">
<mxGeometry x="55" y="550" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="of2" value="&lt;b&gt;2. Redirect&lt;/b&gt;&lt;br&gt;&lt;font style=&quot;font-size:9px&quot;&gt;User autoryzuje&lt;br&gt;na stronie Google/Meta&lt;/font&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#CE93D8;strokeColor=#7B1FA2;fontSize=10;" vertex="1" parent="1">
<mxGeometry x="195" y="550" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="of3" value="&lt;b&gt;3. Callback&lt;/b&gt;&lt;br&gt;&lt;font style=&quot;font-size:9px&quot;&gt;GET /api/oauth/&lt;br&gt;callback/{provider}&lt;br&gt;Exchange code→token&lt;/font&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#CE93D8;strokeColor=#7B1FA2;fontSize=10;" vertex="1" parent="1">
<mxGeometry x="335" y="550" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="of4" value="&lt;b&gt;4. Save&lt;/b&gt;&lt;br&gt;&lt;font style=&quot;font-size:9px&quot;&gt;oauth_tokens table&lt;br&gt;access + refresh&lt;br&gt;Auto-refresh on expiry&lt;/font&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#CE93D8;strokeColor=#7B1FA2;fontSize=10;" vertex="1" parent="1">
<mxGeometry x="475" y="550" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="ofa1" style="strokeColor=#7B1FA2;strokeWidth=2;" edge="1" source="of1" target="of2" parent="1"><mxGeometry relative="1" as="geometry"/></mxCell>
<mxCell id="ofa2" style="strokeColor=#7B1FA2;strokeWidth=2;" edge="1" source="of2" target="of3" parent="1"><mxGeometry relative="1" as="geometry"/></mxCell>
<mxCell id="ofa3" style="strokeColor=#7B1FA2;strokeWidth=2;" edge="1" source="of3" target="of4" parent="1"><mxGeometry relative="1" as="geometry"/></mxCell>
<!-- OAuth providers -->
<mxCell id="google_oauth" value="&lt;b style=&quot;color:#4285F4&quot;&gt;Google OAuth ✅&lt;/b&gt;&lt;br&gt;&lt;font style=&quot;font-size:9px&quot;&gt;GBP: business.manage&lt;br&gt;Search Console: webmasters&lt;br&gt;&lt;b&gt;Działa na produkcji&lt;/b&gt;&lt;/font&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#E3F2FD;strokeColor=#4285F4;fontSize=10;" vertex="1" parent="1">
<mxGeometry x="630" y="548" width="130" height="65" as="geometry"/>
</mxCell>
<mxCell id="meta_oauth" value="&lt;b style=&quot;color:#1877F2&quot;&gt;Meta OAuth ⏳&lt;/b&gt;&lt;br&gt;&lt;font style=&quot;font-size:9px&quot;&gt;Facebook: pages_show_list&lt;br&gt;Instagram: instagram_basic&lt;br&gt;&lt;b&gt;Framework gotowy&lt;/b&gt;&lt;/font&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#E3F2FD;strokeColor=#1877F2;fontSize=10;" vertex="1" parent="1">
<mxGeometry x="770" y="548" width="130" height="65" as="geometry"/>
</mxCell>
<!-- BEZPIECZEŃSTWO (prawy-dół) -->
<mxCell id="sec_bg" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#FFEBEE;strokeColor=#C62828;shadow=1;" vertex="1" parent="1">
<mxGeometry x="40" y="640" width="870" height="90" as="geometry"/>
</mxCell>
<mxCell id="sec_title" value="&lt;b style=&quot;font-size:14px;color:#C62828&quot;&gt;Zabezpieczenia&lt;/b&gt;" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="55" y="646" width="150" height="24" as="geometry"/>
</mxCell>
<mxCell id="sec1" value="&lt;b&gt;Hasła&lt;/b&gt;&lt;br&gt;&lt;font style=&quot;font-size:9px&quot;&gt;pbkdf2:sha256&lt;br&gt;8+ znaków&lt;/font&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff;strokeColor=#C62828;fontSize=10;" vertex="1" parent="1">
<mxGeometry x="55" y="674" width="100" height="45" as="geometry"/>
</mxCell>
<mxCell id="sec2" value="&lt;b&gt;2FA (TOTP)&lt;/b&gt;&lt;br&gt;&lt;font style=&quot;font-size:9px&quot;&gt;Base32 + kody&lt;br&gt;zapasowe&lt;/font&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff;strokeColor=#C62828;fontSize=10;" vertex="1" parent="1">
<mxGeometry x="165" y="674" width="100" height="45" as="geometry"/>
</mxCell>
<mxCell id="sec3" value="&lt;b&gt;Brute-force&lt;/b&gt;&lt;br&gt;&lt;font style=&quot;font-size:9px&quot;&gt;5 prób → 30 min&lt;br&gt;lockout&lt;/font&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff;strokeColor=#C62828;fontSize=10;" vertex="1" parent="1">
<mxGeometry x="275" y="674" width="100" height="45" as="geometry"/>
</mxCell>
<mxCell id="sec4" value="&lt;b&gt;CSRF&lt;/b&gt;&lt;br&gt;&lt;font style=&quot;font-size:9px&quot;&gt;Flask-WTF +&lt;br&gt;OAuth state&lt;/font&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff;strokeColor=#C62828;fontSize=10;" vertex="1" parent="1">
<mxGeometry x="385" y="674" width="100" height="45" as="geometry"/>
</mxCell>
<mxCell id="sec5" value="&lt;b&gt;Rate Limit&lt;/b&gt;&lt;br&gt;&lt;font style=&quot;font-size:9px&quot;&gt;50/h, 200/dzień&lt;br&gt;Flask-Limiter&lt;/font&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff;strokeColor=#C62828;fontSize=10;" vertex="1" parent="1">
<mxGeometry x="495" y="674" width="100" height="45" as="geometry"/>
</mxCell>
<mxCell id="sec6" value="&lt;b&gt;Email 24h&lt;/b&gt;&lt;br&gt;&lt;font style=&quot;font-size:9px&quot;&gt;Weryfikacja +&lt;br&gt;reset token&lt;/font&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff;strokeColor=#C62828;fontSize=10;" vertex="1" parent="1">
<mxGeometry x="605" y="674" width="100" height="45" as="geometry"/>
</mxCell>
<mxCell id="sec7" value="&lt;b&gt;Audit Log&lt;/b&gt;&lt;br&gt;&lt;font style=&quot;font-size:9px&quot;&gt;security_service&lt;br&gt;.log_audit()&lt;/font&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff;strokeColor=#C62828;fontSize=10;" vertex="1" parent="1">
<mxGeometry x="715" y="674" width="100" height="45" as="geometry"/>
</mxCell>
<!-- DECORATORY -->
<mxCell id="dec_bg" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#E0F7FA;strokeColor=#00838F;shadow=1;" vertex="1" parent="1">
<mxGeometry x="960" y="85" width="280" height="380" as="geometry"/>
</mxCell>
<mxCell id="dec_title" value="&lt;b style=&quot;font-size:14px;color:#00838F&quot;&gt;Dekoratory Flask&lt;/b&gt;&lt;br&gt;&lt;font style=&quot;font-size:10px&quot;&gt;utils/decorators.py&lt;/font&gt;" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="975" y="91" width="200" height="36" as="geometry"/>
</mxCell>
<mxCell id="d1" value="&lt;b&gt;@login_required&lt;/b&gt;&lt;br&gt;&lt;font style=&quot;font-size:9px&quot;&gt;Flask-Login — redirect do /login&lt;/font&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff;strokeColor=#00838F;fontSize=10;align=left;spacingLeft=6;" vertex="1" parent="1">
<mxGeometry x="975" y="134" width="250" height="36" as="geometry"/>
</mxCell>
<mxCell id="d2" value="&lt;b&gt;@role_required(SystemRole.X)&lt;/b&gt;&lt;br&gt;&lt;font style=&quot;font-size:9px&quot;&gt;Minimalny poziom roli → 403&lt;/font&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff;strokeColor=#00838F;fontSize=10;align=left;spacingLeft=6;" vertex="1" parent="1">
<mxGeometry x="975" y="178" width="250" height="36" as="geometry"/>
</mxCell>
<mxCell id="d3" value="&lt;b&gt;@admin_required&lt;/b&gt;&lt;br&gt;&lt;font style=&quot;font-size:9px&quot;&gt;Alias: @role_required(ADMIN)&lt;/font&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff;strokeColor=#00838F;fontSize=10;align=left;spacingLeft=6;" vertex="1" parent="1">
<mxGeometry x="975" y="222" width="250" height="36" as="geometry"/>
</mxCell>
<mxCell id="d4" value="&lt;b&gt;@member_required&lt;/b&gt;&lt;br&gt;&lt;font style=&quot;font-size:9px&quot;&gt;Alias: @role_required(MEMBER)&lt;/font&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff;strokeColor=#00838F;fontSize=10;align=left;spacingLeft=6;" vertex="1" parent="1">
<mxGeometry x="975" y="266" width="250" height="36" as="geometry"/>
</mxCell>
<mxCell id="d5" value="&lt;b&gt;@company_permission('edit')&lt;/b&gt;&lt;br&gt;&lt;font style=&quot;font-size:9px&quot;&gt;Delegowane uprawnienia firmy&lt;/font&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff;strokeColor=#00838F;fontSize=10;align=left;spacingLeft=6;" vertex="1" parent="1">
<mxGeometry x="975" y="310" width="250" height="36" as="geometry"/>
</mxCell>
<mxCell id="dec_methods" value="&lt;font style=&quot;font-size:10px;color:#00838F&quot;&gt;&lt;b&gt;Metody User:&lt;/b&gt;&lt;br&gt;.has_role(SystemRole.X)&lt;br&gt;.can_edit_company(id)&lt;br&gt;.can_manage_company(id)&lt;br&gt;.can_access_admin_panel()&lt;br&gt;.has_delegated_permission()&lt;/font&gt;" style="text;html=1;align=left;verticalAlign=top;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="975" y="358" width="200" height="86" as="geometry"/>
</mxCell>
<!-- PODSUMOWANIE -->
<mxCell id="summary" value="&lt;font style=&quot;font-size:11px;color:#888&quot;&gt;&lt;b&gt;6 ról systemowych&lt;/b&gt; (hierarchiczne) | &lt;b&gt;3 role firmowe&lt;/b&gt; (per company) | &lt;b&gt;7 delegowanych uprawnień&lt;/b&gt; | &lt;b&gt;OAuth 2.0&lt;/b&gt; (Google ✅ + Meta ⏳) | &lt;b&gt;7 zabezpieczeń&lt;/b&gt;&lt;/font&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" vertex="1" parent="1">
<mxGeometry x="250" y="760" width="820" height="24" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>