1use crate::{AuthenticatorStatus, AuthenticatorTransport, UserVerificationMethod};
7use std::str::FromStr;
8use uuid::Uuid;
9
10#[derive(Debug, PartialEq)]
11pub enum AttrValueAssertion {
12 AaguidEq(Uuid),
13 DescriptionEq(String),
14 DescriptionCnt(String),
15 StatusEq(AuthenticatorStatus),
16 StatusGte(AuthenticatorStatus),
17 StatusLt(AuthenticatorStatus),
18 TransportEq(AuthenticatorTransport),
19 UserVerificationCnt(UserVerificationMethod),
20}
21
22#[derive(Debug, PartialEq)]
23pub enum Query {
24 Op(AttrValueAssertion),
25 And(Box<Query>, Box<Query>),
26 Or(Box<Query>, Box<Query>),
27 Not(Box<Query>),
28}
29
30impl Query {
31 pub fn exclude_compromised_devices() -> Self {
32 Query::Not(Box::new(Query::Op(AttrValueAssertion::StatusLt(
33 AuthenticatorStatus::FidoCertified,
34 ))))
35 }
36}
37
38impl FromStr for Query {
39 type Err = peg::error::ParseError<peg::str::LineCol>;
40
41 fn from_str(q: &str) -> Result<Self, Self::Err> {
42 query::parse(q)
43 }
44}
45
46peg::parser! {
47 grammar query() for str {
48 pub rule parse() -> Query = precedence!{
49 a:(@) separator()+ "or" separator()+ b:@ {
50 Query::Or(
51 Box::new(a),
52 Box::new(b)
53 )
54 }
55 --
56 a:(@) separator()+ "and" separator()+ b:@ {
57 Query::And(
58 Box::new(a),
59 Box::new(b)
60 )
61 }
62 --
63 "not" separator()+ "(" e:parse() ")" {
64 Query::Not(Box::new(e))
65 }
66 --
67 "(" e:parse() ")" { e }
68 a:expr() { a }
69 }
70
71 rule separator() =
72 ['\n' | ' ' | '\t' ]
73
74 rule operator() =
75 ['\n' | ' ' | '\t' | '(' | ')' ]
76
77 pub(crate) rule expr() -> Query =
78 uuid_eq_expr() /
79 desc_eq_expr() /
80 desc_cn_expr() /
81 authstat_eq_expr() /
82 authstat_gte_expr() /
83 authstat_lt_expr() /
84 authtrans_eq_expr() /
85 uvm_cnt_expr()
86
87 rule uuid_eq_expr() -> Query =
88 "aaguid" separator()+ "eq" separator()+ v:uuid() { Query::Op(AttrValueAssertion::AaguidEq(v)) }
89
90 rule desc_eq_expr() -> Query =
91 "desc" separator()+ "eq" separator()+ v:octetstr() { Query::Op(AttrValueAssertion::DescriptionEq(v)) }
92
93 rule desc_cn_expr() -> Query =
94 "desc" separator()+ "cnt" separator()+ v:octetstr() { Query::Op(AttrValueAssertion::DescriptionCnt(v)) }
95
96 rule authstat_eq_expr() -> Query =
97 "status" separator()+ "eq" separator()+ v:status() { Query::Op(AttrValueAssertion::StatusEq(v)) }
98
99 rule authstat_gte_expr() -> Query =
100 "status" separator()+ "gte" separator()+ v:status() { Query::Op(AttrValueAssertion::StatusGte(v)) }
101
102 rule authstat_lt_expr() -> Query =
103 "status" separator()+ "lt" separator()+ v:status() { Query::Op(AttrValueAssertion::StatusLt(v)) }
104
105 rule authtrans_eq_expr() -> Query =
106 "transport" separator()+ "eq" separator()+ v:transport() { Query::Op(AttrValueAssertion::TransportEq(v)) }
107
108 rule uvm_cnt_expr() -> Query =
109 "uvm" separator()+ "cnt" separator()+ v:uvm() { Query::Op(AttrValueAssertion::UserVerificationCnt(v)) }
110
111 pub(crate) rule uuid() -> Uuid =
112 s:$((!operator()[_])+) {? Uuid::from_str(s).map_err(|_| "invalid UUID" ) }
113
114 pub(crate) rule status() -> AuthenticatorStatus =
115 s:$((!operator()[_])+) {? AuthenticatorStatus::from_str(s).map_err(|_| "invalid Authenticator Status" ) }
116
117 pub(crate) rule transport() -> AuthenticatorTransport =
118 s:$((!operator()[_])+) {? AuthenticatorTransport::from_str(s).map_err(|_| "invalid Authenticator Transport" ) }
119
120 pub(crate) rule uvm() -> UserVerificationMethod =
121 s:$((!operator()[_])+) {? UserVerificationMethod::from_str(s).map_err(|_| "invalid User Verification Method" ) }
122
123 pub(crate) rule octetstr() -> String =
124 dquotedoctetstr() / squotedoctetstr() / bareoctetstr()
125
126 rule squotedoctetstr() -> String =
127 "\'" s:$((!"\'"[_])*) "\'" { s.to_string() }
128
129 rule dquotedoctetstr() -> String =
130 "\"" s:$((!"\""[_])*) "\"" { s.to_string() }
131
132 rule bareoctetstr() -> String =
133 s:$((!operator()[_])*) { s.to_string() }
134 }
135}
136
137#[cfg(test)]
138mod test {
139 use super::*;
140
141 #[test]
142 fn test_attr_uuid() {
143 assert_eq!(
144 query::uuid("c370f859-622e-4388-9ad2-a7fe7551fdba"),
145 Ok(uuid::uuid!("c370f859-622e-4388-9ad2-a7fe7551fdba"))
146 );
147 assert!(query::uuid("oueuntonaeunaun").is_err());
148 }
149
150 #[test]
151 fn test_query_attr_uuid() {
152 assert_eq!(
153 query::expr("aaguid eq c370f859-622e-4388-9ad2-a7fe7551fdba"),
154 Ok(Query::Op(AttrValueAssertion::AaguidEq(uuid::uuid!(
155 "c370f859-622e-4388-9ad2-a7fe7551fdba"
156 ))))
157 );
158 }
159
160 #[test]
161 fn test_query_not() {
162 assert_eq!(
163 query::parse("not (aaguid eq c370f859-622e-4388-9ad2-a7fe7551fdba)"),
164 Ok(Query::Not(Box::new(Query::Op(
165 AttrValueAssertion::AaguidEq(uuid::uuid!("c370f859-622e-4388-9ad2-a7fe7551fdba"))
166 ))))
167 );
168 }
169
170 #[test]
171 fn test_query_and() {
172 assert_eq!(
173 query::parse("aaguid eq c370f859-622e-4388-9ad2-a7fe7551fdba and aaguid eq 70f11dce-befb-4619-a091-110633d923f6"),
174 Ok(
175 Query::And(
176 Box::new(
177 Query::Op(
178 AttrValueAssertion::AaguidEq(uuid::uuid!("c370f859-622e-4388-9ad2-a7fe7551fdba"))
179 )
180 ),
181 Box::new(
182 Query::Op(
183 AttrValueAssertion::AaguidEq(uuid::uuid!("70f11dce-befb-4619-a091-110633d923f6"))
184 )
185 ),
186 )
187 )
188 );
189 }
190
191 #[test]
192 fn test_query_or() {
193 assert_eq!(
194 query::parse("aaguid eq c370f859-622e-4388-9ad2-a7fe7551fdba or aaguid eq 70f11dce-befb-4619-a091-110633d923f6"),
195 Ok(
196 Query::Or(
197 Box::new(
198 Query::Op(
199 AttrValueAssertion::AaguidEq(uuid::uuid!("c370f859-622e-4388-9ad2-a7fe7551fdba"))
200 )
201 ),
202 Box::new(
203 Query::Op(
204 AttrValueAssertion::AaguidEq(uuid::uuid!("70f11dce-befb-4619-a091-110633d923f6"))
205 )
206 ),
207 )
208 )
209 );
210 }
211}