• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Unleash / unleash-edge / 15441100167

04 Jun 2025 11:28AM UTC coverage: 78.265% (+10.3%) from 67.995%
15441100167

Pull #970

github

web-flow
Merge 8ad457ed6 into 34ad3228b
Pull Request #970: task(rust): Update Rust version to 1.87.0

10140 of 12956 relevant lines covered (78.26%)

158.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.89
/server/src/middleware/validate_token.rs
1
use crate::auth::token_validator::{TokenRegister, TokenValidator};
2
use crate::error::EdgeError;
3
use crate::types::{EdgeToken, TokenType, TokenValidationStatus};
4
use actix_web::{
5
    HttpResponse,
6
    body::MessageBody,
7
    dev::{ServiceRequest, ServiceResponse},
8
    web::Data,
9
};
10
use dashmap::DashMap;
11

12
pub async fn validate_token(
84✔
13
    token: EdgeToken,
84✔
14
    req: ServiceRequest,
84✔
15
    srv: crate::middleware::as_async_middleware::Next<impl MessageBody + 'static>,
84✔
16
) -> Result<ServiceResponse<impl MessageBody>, actix_web::Error> {
84✔
17
    let maybe_validator = req.app_data::<Data<TokenValidator>>();
84✔
18
    let token_cache = req
84✔
19
        .app_data::<Data<DashMap<String, EdgeToken>>>()
84✔
20
        .unwrap()
84✔
21
        .clone()
84✔
22
        .into_inner();
84✔
23

24
    let validation_status = validate(&token, maybe_validator, &token_cache, req.path()).await;
84✔
25

26
    match validation_status {
84✔
27
        Ok(_) => Ok(srv.call(req).await?.map_into_left_body()),
78✔
28
        Err(err) => match err {
6✔
29
            EdgeError::AuthorizationDenied => Ok(req
×
30
                .into_response(HttpResponse::Unauthorized().finish())
×
31
                .map_into_right_body()),
×
32
            EdgeError::Forbidden(_) => Ok(req
6✔
33
                .into_response(HttpResponse::Forbidden().finish())
6✔
34
                .map_into_right_body()),
6✔
35
            _ => Err(err.into()),
×
36
        },
37
    }
38
}
84✔
39

40
async fn validate(
92✔
41
    token: &EdgeToken,
92✔
42
    maybe_validator: Option<&Data<impl TokenRegister>>,
92✔
43
    token_cache: &DashMap<String, EdgeToken>,
92✔
44
    path: &str,
92✔
45
) -> Result<(), EdgeError> {
92✔
46
    if token.status == TokenValidationStatus::Trusted {
92✔
47
        return Ok(());
1✔
48
    }
91✔
49

91✔
50
    match maybe_validator {
91✔
51
        Some(validator) => {
32✔
52
            let known_token = validator.register_token(token.token.clone()).await?;
32✔
53
            match known_token.status {
31✔
54
                TokenValidationStatus::Validated => match known_token.token_type {
31✔
55
                    Some(TokenType::Frontend) => check_frontend_path(path),
5✔
56
                    Some(TokenType::Client) => check_backend_path(path),
26✔
57
                    _ => Err(EdgeError::Forbidden("".into())),
×
58
                },
59

60
                TokenValidationStatus::Unknown => Err(EdgeError::AuthorizationDenied),
×
61
                TokenValidationStatus::Invalid => Err(EdgeError::Forbidden("".into())),
×
62
                TokenValidationStatus::Trusted => unreachable!(),
×
63
            }
64
        }
65
        None => match token_cache.get(&token.token) {
59✔
66
            Some(t) => {
54✔
67
                let token = t.value();
54✔
68
                match token.token_type {
32✔
69
                    Some(TokenType::Frontend) => check_frontend_path(path),
2✔
70
                    Some(TokenType::Client) => check_backend_path(path),
30✔
71
                    None => Ok(()),
22✔
72
                    _ => Err(EdgeError::Forbidden("".into())),
×
73
                }
74
            }
75
            None => Err(EdgeError::Forbidden("".into())),
5✔
76
        },
77
    }
78
}
92✔
79

80
fn check_frontend_path(path: &str) -> Result<(), EdgeError> {
7✔
81
    if path.contains("/api/frontend") || path.contains("/api/proxy") {
7✔
82
        Ok(())
5✔
83
    } else {
84
        Err(EdgeError::Forbidden("".into()))
2✔
85
    }
86
}
7✔
87

88
fn check_backend_path(path: &str) -> Result<(), EdgeError> {
56✔
89
    if path.contains("/api/client") {
56✔
90
        Ok(())
54✔
91
    } else {
92
        Err(EdgeError::Forbidden("".into()))
2✔
93
    }
94
}
56✔
95

96
#[cfg(test)]
97
mod tests {
98
    use std::str::FromStr;
99

100
    use super::*;
101

102
    struct FrontendValidator {}
103

104
    impl TokenRegister for FrontendValidator {
105
        async fn register_token(&self, token: String) -> crate::types::EdgeResult<EdgeToken> {
3✔
106
            Ok(EdgeToken {
3✔
107
                status: TokenValidationStatus::Validated,
3✔
108
                token_type: Some(TokenType::Frontend),
3✔
109
                ..EdgeToken::from_str(&token).unwrap()
3✔
110
            })
3✔
111
        }
3✔
112
    }
113

114
    struct ClientValidator {}
115

116
    impl TokenRegister for ClientValidator {
117
        async fn register_token(&self, token: String) -> crate::types::EdgeResult<EdgeToken> {
3✔
118
            Ok(EdgeToken {
3✔
119
                status: TokenValidationStatus::Validated,
3✔
120
                token_type: Some(TokenType::Client),
3✔
121
                ..EdgeToken::from_str(&token).unwrap()
3✔
122
            })
3✔
123
        }
3✔
124
    }
125

126
    struct FailValidator {}
127

128
    impl TokenRegister for FailValidator {
129
        async fn register_token(&self, _token: String) -> crate::types::EdgeResult<EdgeToken> {
1✔
130
            Err(EdgeError::EdgeTokenError)
1✔
131
        }
1✔
132
    }
133

134
    #[actix_web::test]
135
    async fn validation_always_allows_trusted_tokens() {
136
        let token = EdgeToken {
137
            token: "legacy-123".into(),
138
            status: TokenValidationStatus::Trusted,
139
            token_type: Some(TokenType::Frontend),
140
            ..Default::default()
141
        };
142

143
        let result = validate(
144
            &token,
145
            Some(&Data::new(FailValidator {})),
146
            &DashMap::new(),
147
            "/api/frontend/some_path",
148
        )
149
        .await;
150

151
        assert!(result.is_ok());
152
    }
153

154
    #[actix_web::test]
155
    async fn validation_denies_frontend_tokens_on_backend_paths() {
156
        let token = EdgeToken {
157
            token: "*:development.somesecretstring".into(),
158
            status: TokenValidationStatus::Validated,
159
            token_type: Some(TokenType::Frontend),
160
            ..Default::default()
161
        };
162

163
        let hit_features = validate(
164
            &token,
165
            Some(&Data::new(FrontendValidator {})),
166
            &DashMap::new(),
167
            "/api/client/features",
168
        )
169
        .await;
170

171
        assert!(hit_features.is_err());
172
    }
173

174
    #[actix_web::test]
175
    async fn validation_allows_frontend_tokens_on_frontend_paths() {
176
        let token = EdgeToken {
177
            token: "*:development.somesecretstring".into(),
178
            status: TokenValidationStatus::Validated,
179
            token_type: Some(TokenType::Frontend),
180
            ..Default::default()
181
        };
182

183
        let hit_frontend = validate(
184
            &token,
185
            Some(&Data::new(FrontendValidator {})),
186
            &DashMap::new(),
187
            "/api/frontend",
188
        )
189
        .await;
190

191
        let hit_proxy = validate(
192
            &token,
193
            Some(&Data::new(FrontendValidator {})),
194
            &DashMap::new(),
195
            "/api/proxy",
196
        )
197
        .await;
198

199
        assert!(hit_frontend.is_ok());
200
        assert!(hit_proxy.is_ok());
201
    }
202

203
    #[actix_web::test]
204
    async fn validation_denies_client_tokens_on_frontend_paths() {
205
        let token = EdgeToken {
206
            token: "*:development.somesecretstring".into(),
207
            status: TokenValidationStatus::Validated,
208
            token_type: Some(TokenType::Client),
209
            ..Default::default()
210
        };
211

212
        let hit_frontend = validate(
213
            &token,
214
            Some(&Data::new(ClientValidator {})),
215
            &DashMap::new(),
216
            "/api/frontend",
217
        )
218
        .await;
219

220
        let hit_proxy = validate(
221
            &token,
222
            Some(&Data::new(ClientValidator {})),
223
            &DashMap::new(),
224
            "/api/proxy",
225
        )
226
        .await;
227
        assert!(hit_frontend.is_err());
228
        assert!(hit_proxy.is_err());
229
    }
230

231
    #[actix_web::test]
232
    async fn validation_allows_client_tokens_on_backend_paths() {
233
        let token = EdgeToken {
234
            token: "*:development.somesecretstring".into(),
235
            status: TokenValidationStatus::Validated,
236
            token_type: Some(TokenType::Client),
237
            ..Default::default()
238
        };
239

240
        let hit_features = validate(
241
            &token,
242
            Some(&Data::new(ClientValidator {})),
243
            &DashMap::new(),
244
            "/api/client/features",
245
        )
246
        .await;
247

248
        assert!(hit_features.is_ok());
249
    }
250

251
    #[actix_web::test]
252
    async fn broken_token_bubbles_error() {
253
        let token = EdgeToken {
254
            token: "totally-broken-token".into(),
255
            status: TokenValidationStatus::Invalid,
256
            ..Default::default()
257
        };
258

259
        let result = validate(
260
            &token,
261
            Some(&Data::new(FailValidator {})),
262
            &DashMap::new(),
263
            "/api/client/features",
264
        )
265
        .await;
266

267
        match result {
268
            Err(EdgeError::EdgeTokenError) => {}
269
            _ => panic!("Expected a token error"),
270
        }
271
    }
272
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc