| security | by dg | User: authorization is decided by voters instead of an authorizator per role (BC break) A Voter decides about a Principal, the effective state of the user: the identity only while logged in (unlike User::getId(), which keeps returning the retained identity after logout), the effective roles and the voters, so that a voter may delegate through $principal->isAllowed(). Ownership is checked by Principal::hasId(), which never matches for a guest. A vote is a Vote: a single Forbid wins, otherwise a single Allow suffices. Vote::allowIf() allows or abstains, so forbidding is always written on purpose. User::createPrincipal() decides about someone else than the current user, or about a visitor for null, taking the identity as it is; getPrincipal() is built on it. Permission is one of the voters; its vote() ORs the roles, forbids when an explicit deny rule stopped them and abstains when only the default deny-all did, as well as on roles and resources the ACL does not know instead of throwing. SecurityExtension adds every service implementing Voter to the user. setAuthorizator() and the getters are deprecated; the third parameter of the User constructor is a variadic list of voters, User::isAllowed() types the privilege as ?string. | | | |
| security | by dg | Role is deprecated, the core works with role names only (BC break) Roles are names. An identity returning a Role object throws InvalidStateException, Permission::isAllowed() accepts the role only as a string, and getQueriedRole() and getQueriedResource() return names, so an assertion decides by names only. Resource stays: it maps an entity to the name of an ACL resource, so the rules of that resource apply to the entity. | | | |
| security | by dg | deprecated wip | | | |
| security | by dg | CookieIdentity WIP | | | |
| security | by dg | IdentityHandler: make getGuestIdentity() a regular interface method (BC break) Promotes the optional @method to a required method, so User no longer needs method_exists() to call it. BC break: existing IdentityHandler implementations must now declare getGuestIdentity(). | | | |
| security | by dg | User: deprecated magic properties (BC break) | | | |
| security | by dg | SimpleIdentity: reading an undeclared property throws (BC break) Until now __get() returned null for an unknown key and, because it returns by reference, silently created that key in the data (and thus in the storage). It now throws MemberAccessException, like any other Nette object. This also ends the by-ref write pattern $identity->cart[] = $item on a key that has not been set yet. | | | |
| security | by dg | SimpleIdentity: uses __serialize & __unserialize | | | |
| security | by dg | IIdentity: make getData() a regular interface method (BC break) | | | |
| mail | by dg | Message: Message-ID takes the domain of the sender The domain came from $_SERVER['HTTP_HOST'] or php_uname(), i.e. from a header the client controls (with a port it turned into example.com8080) or from the name of the machine, which in CLI and containers is a random string, neither of which says where the message comes from. The domain is now taken from the Sender address, or from From, the way other mail libraries do it; the old source remains for a message with neither, and for an internationalized domain left unconverted without ext-intl. | | | |
| mail | by dg | MailPanel: previews the body and attachments of each message The panel listed only the envelope, so checking what a message actually looked like meant sending it somewhere first. Each message now has a collapsed preview: the HTML body in a sandboxed iframe, the text body, and the attachments by name, type and size. Embedded images are referenced by cid:, which means nothing to a browser, so they are inlined as data: URIs and the preview shows what the recipient sees. Message::getEmbeddedFiles() exposes the embedded parts, the counterpart of getAttachments(). | | | |
| mail | by dg | CssInliner: writes every candidate of a property, the winner last Email CSS leans on fallbacks: display: -webkit-box; display: flex works because a client drops a value it does not understand and the previous declaration applies. Writing only the cascade winner left an old client with nothing. The commonest case, a fallback within one rule, was lost even earlier, in parsing, which kept the declarations of a block in a property => value map. A block keeps its duplicate properties, and inline() writes all candidates of a property ordered by rank, the winner last, so the client resolves the rest the way a browser would, fallbacks across rules included. A candidate whose value a higher-ranked one repeats is dropped, since it can never matter. getRules() keeps its shape and returns the last value of each property. | | | |
| mail | by dg | MailExtension: the accessToken key enables XOAUTH2 Setting an access token meant a setup: entry on the transport service, whose name is mail.mailer or mail.innerMailer depending on whether redirect or the debugger is on. The accessToken key passes the token to setAccessToken() on whichever it is. It takes anything DI evaluates; the useful form is a callable such as @App\GoogleOAuth::getAccessToken(...), resolved on every connection so an expiring token gets refreshed. A token configured without the SMTP transport is a configuration error rather than silently ignored. | | | |
| mail | by dg | MailExtension: the file key writes emails to .eml files FileMailer could only be used by registering mail.mailer by hand, which also gave up everything the extension wires around the transport: the DKIM signer, the Interceptor for redirect and debugger, and the Tracy panel. For development that is the configuration people want first. With `file: <dir>` the transport is FileMailer, taking precedence over smtp and sendmail, and the rest is wired around it as for any other transport. | | | |
| mail | by dg | SmtpMailer: rejects an unknown encryption (BC break) Any value of encryption other than 'ssl' or 'tls' was silently treated as none, so 'TLS', 'SSL' or 'starttls' connected without encryption to port 25. Depending on the server, sending then failed with a misleading "does not support authentication", or the credentials and the message went out in plain text. The DI schema does not catch it when the value comes from a dynamic parameter such as %env%. The constructor now throws InvalidArgumentException, so a configuration that used to send unencrypted fails instead. | | | |
| tracy | by dg | used attribute Deprecated | | | |
| tracy | by dg | error.log changed to warning.log | | | |
| tracy | by dg | Logger: added typehints WIP | | | |
| tracy | by dg | open in editor: added support for columns | | | |
| tracy | by dg | opened 3.0-dev | | | |
| security | by dg | Identity -> SimpleIdentity Identity class must exist as an alias in order to read it from session | | | |
| security | by dg | IIdentity: added return typehint (BC break) | | | |
| security | by dg | removed deprecated IAuthenticator (BC break) | | | |
| security | by dg | removed legacy services names (BC break) | | | |
| security | by dg | used PHP 8.2 features | | | |
| security | by dg | uses nette/http 4 | | | |
| security | by dg | requires PHP 8.3 | | | |
| security | by dg | opened 4.0-dev | | | |
| security | by dg | uses dresscode | | | |
| security | by dg | Permission: fixed whole-resource query ignoring a privilege deny on a parent resource | | | |
| forms | by dg | BaseControl: added setFixedValue() for a value the user cannot change | | | |
| forms | by dg | netteForms: removed webalize() (BC break) | | | |