Skip to main content

actix_security_codegen/
lib.rs

1//! Procedural macros for Spring Security-like method-level security.
2//!
3//! # Spring Security Equivalents
4//!
5//! | Spring Security | actix-security-codegen |
6//! |-----------------|------------------------|
7//! | `@Secured("ROLE_ADMIN")` | `#[secured("ADMIN")]` |
8//! | `@PreAuthorize("hasRole('ADMIN')")` | `#[pre_authorize(role = "ADMIN")]` |
9//! | `@PreAuthorize("hasAuthority('read')")` | `#[pre_authorize(authority = "read")]` |
10//! | `@PreAuthorize("isAuthenticated()")` | `#[pre_authorize(authenticated)]` |
11//! | `@PermitAll` | `#[permit_all]` |
12//! | `@DenyAll` | `#[deny_all]` |
13//! | `@RolesAllowed({"ADMIN"})` | `#[roles_allowed("ADMIN")]` |
14//!
15//! # Usage
16//!
17//! ```ignore
18//! use actix_security_codegen::{secured, pre_authorize};
19//! use actix_security_core::http::security::AuthenticatedUser;
20//! use actix_web::{get, HttpResponse, Responder};
21//!
22//! // Role-based security (like @Secured)
23//! #[secured("ADMIN")]
24//! #[get("/admin")]
25//! async fn admin_only(user: AuthenticatedUser) -> impl Responder {
26//!     HttpResponse::Ok().body("Admin area")
27//! }
28//!
29//! // Authority-based security (like @PreAuthorize)
30//! #[pre_authorize(authority = "users:read")]
31//! #[get("/api/users")]
32//! async fn get_users(user: AuthenticatedUser) -> impl Responder {
33//!     HttpResponse::Ok().body("Users")
34//! }
35//!
36//! // Authentication check only
37//! #[pre_authorize(authenticated)]
38//! #[get("/protected")]
39//! async fn protected(user: AuthenticatedUser) -> impl Responder {
40//!     HttpResponse::Ok().body("Protected")
41//! }
42//! ```
43
44use proc_macro::TokenStream;
45
46// Internal modules
47mod access;
48mod helpers;
49mod legacy;
50mod pre_authorize;
51mod secured;
52mod simple;
53
54// =============================================================================
55// Primary Macros (Spring Security-like)
56// =============================================================================
57
58/// Role-based method security annotation.
59///
60/// # Spring Security Equivalent
61/// `@Secured("ROLE_ADMIN")` or `@RolesAllowed("ADMIN")`
62///
63/// # Usage
64/// ```ignore
65/// use actix_security_core::http::security::AuthenticatedUser;
66/// use actix_security_codegen::secured;
67///
68/// // Single role
69/// #[secured("ADMIN")]
70/// #[get("/admin")]
71/// async fn admin_only(user: AuthenticatedUser) -> impl Responder {
72///     HttpResponse::Ok().body("Admin area")
73/// }
74///
75/// // Multiple roles (OR logic - user needs ANY of these)
76/// #[secured("ADMIN", "MANAGER")]
77/// #[get("/management")]
78/// async fn management(user: AuthenticatedUser) -> impl Responder {
79///     HttpResponse::Ok().body("Management area")
80/// }
81/// ```
82///
83/// # Note
84/// Unlike Spring Security, you don't need the "ROLE_" prefix.
85/// The macro checks if the user has ANY of the specified roles.
86#[proc_macro_attribute]
87pub fn secured(attrs: TokenStream, input: TokenStream) -> TokenStream {
88    secured::secured_impl(attrs, input)
89}
90
91/// Flexible method security annotation with SpEL-like expressions.
92///
93/// # Spring Security Equivalent
94/// `@PreAuthorize("...")`
95///
96/// # Supported Expressions
97///
98/// | Actix Security | Spring Security |
99/// |----------------|-----------------|
100/// | `#[pre_authorize(authenticated)]` | `@PreAuthorize("isAuthenticated()")` |
101/// | `#[pre_authorize(role = "ADMIN")]` | `@PreAuthorize("hasRole('ADMIN')")` |
102/// | `#[pre_authorize(roles = ["A", "B"])]` | `@PreAuthorize("hasAnyRole('A', 'B')")` |
103/// | `#[pre_authorize(authority = "read")]` | `@PreAuthorize("hasAuthority('read')")` |
104/// | `#[pre_authorize(authorities = ["r", "w"])]` | `@PreAuthorize("hasAnyAuthority('r', 'w')")` |
105///
106/// # Usage
107/// ```ignore
108/// use actix_security_core::http::security::AuthenticatedUser;
109/// use actix_security_codegen::pre_authorize;
110///
111/// // Check authentication only
112/// #[pre_authorize(authenticated)]
113/// #[get("/protected")]
114/// async fn protected(user: AuthenticatedUser) -> impl Responder {
115///     HttpResponse::Ok().body("Protected")
116/// }
117///
118/// // Check single role
119/// #[pre_authorize(role = "ADMIN")]
120/// #[get("/admin")]
121/// async fn admin(user: AuthenticatedUser) -> impl Responder {
122///     HttpResponse::Ok().body("Admin")
123/// }
124///
125/// // Check multiple roles (OR logic)
126/// #[pre_authorize(roles = ["ADMIN", "MANAGER"])]
127/// #[get("/management")]
128/// async fn management(user: AuthenticatedUser) -> impl Responder {
129///     HttpResponse::Ok().body("Management")
130/// }
131///
132/// // Check authority
133/// #[pre_authorize(authority = "users:read")]
134/// #[get("/api/users")]
135/// async fn get_users(user: AuthenticatedUser) -> impl Responder {
136///     HttpResponse::Ok().body("Users")
137/// }
138///
139/// // Check multiple authorities (OR logic)
140/// #[pre_authorize(authorities = ["users:read", "users:write"])]
141/// #[get("/api/users/manage")]
142/// async fn manage_users(user: AuthenticatedUser) -> impl Responder {
143///     HttpResponse::Ok().body("Manage users")
144/// }
145/// ```
146#[proc_macro_attribute]
147pub fn pre_authorize(attrs: TokenStream, input: TokenStream) -> TokenStream {
148    pre_authorize::pre_authorize_impl(attrs, input)
149}
150
151/// Marks an endpoint as publicly accessible (no authentication required).
152///
153/// # Spring Security / Java EE Equivalent
154/// `@PermitAll`
155///
156/// # Usage
157/// ```ignore
158/// use actix_security_codegen::permit_all;
159///
160/// #[permit_all]
161/// #[get("/public")]
162/// async fn public_endpoint() -> impl Responder {
163///     HttpResponse::Ok().body("Public content")
164/// }
165/// ```
166///
167/// # Note
168/// This macro simply passes through to the original function.
169/// No authentication or authorization checks are performed.
170#[proc_macro_attribute]
171pub fn permit_all(attrs: TokenStream, input: TokenStream) -> TokenStream {
172    simple::permit_all_impl(attrs, input)
173}
174
175/// Marks an endpoint as completely inaccessible (always returns 403 Forbidden).
176///
177/// # Spring Security / Java EE Equivalent
178/// `@DenyAll`
179///
180/// # Usage
181/// ```ignore
182/// use actix_security_codegen::deny_all;
183/// use actix_security_core::http::security::AuthenticatedUser;
184///
185/// #[deny_all]
186/// #[get("/disabled")]
187/// async fn disabled_endpoint(user: AuthenticatedUser) -> impl Responder {
188///     HttpResponse::Ok().body("Never reached")
189/// }
190/// ```
191///
192/// # Note
193/// Useful for temporarily disabling endpoints or marking them as under construction.
194#[proc_macro_attribute]
195pub fn deny_all(attrs: TokenStream, input: TokenStream) -> TokenStream {
196    simple::deny_all_impl(attrs, input)
197}
198
199/// Role-based method security annotation (Java EE standard).
200///
201/// # Java EE Equivalent
202/// `@RolesAllowed({"ADMIN", "USER"})`
203///
204/// # Spring Security Equivalent
205/// `@Secured({"ROLE_ADMIN", "ROLE_USER"})`
206///
207/// # Usage
208/// ```ignore
209/// use actix_security_codegen::roles_allowed;
210/// use actix_security_core::http::security::AuthenticatedUser;
211///
212/// #[roles_allowed("ADMIN")]
213/// #[get("/admin")]
214/// async fn admin_only(user: AuthenticatedUser) -> impl Responder {
215///     HttpResponse::Ok().body("Admin area")
216/// }
217///
218/// #[roles_allowed("ADMIN", "MANAGER")]
219/// #[get("/management")]
220/// async fn management(user: AuthenticatedUser) -> impl Responder {
221///     HttpResponse::Ok().body("Management area")
222/// }
223/// ```
224///
225/// # Note
226/// This is an alias for `#[secured]` following Java EE naming conventions.
227#[proc_macro_attribute]
228pub fn roles_allowed(attrs: TokenStream, input: TokenStream) -> TokenStream {
229    simple::roles_allowed_impl(attrs, input)
230}
231
232// =============================================================================
233// Legacy Macros (Deprecated)
234// =============================================================================
235
236/// **Deprecated**: Use `#[secured("ROLE")]` instead.
237///
238/// This macro is kept for backward compatibility.
239#[proc_macro_attribute]
240#[deprecated(since = "0.3.0", note = "Use #[secured] instead")]
241pub fn has_role(attrs: TokenStream, input: TokenStream) -> TokenStream {
242    legacy::has_role_impl(attrs, input)
243}
244
245/// **Deprecated**: Use `#[pre_authorize(authority = "...")]` instead.
246///
247/// This macro is kept for backward compatibility.
248#[proc_macro_attribute]
249#[deprecated(
250    since = "0.3.0",
251    note = "Use #[pre_authorize(authority = \"...\")] instead"
252)]
253pub fn has_access(attrs: TokenStream, input: TokenStream) -> TokenStream {
254    legacy::has_access_impl(attrs, input)
255}
256
257/// **Deprecated**: Use `#[pre_authorize(authenticated)]` instead.
258#[proc_macro_attribute]
259#[deprecated(since = "0.3.0", note = "Use #[pre_authorize(authenticated)] instead")]
260pub fn authenticated(attrs: TokenStream, input: TokenStream) -> TokenStream {
261    legacy::authenticated_impl(attrs, input)
262}