gatekeeper_members/
lib.rs1pub use gatekeeper_core::RealmType;
2use gatekeeper_core::{GatekeeperReader, NfcTag, Realm, UndifferentiatedTag};
3use reqwest::header::AUTHORIZATION;
4use reqwest::StatusCode;
5use serde_json::Value;
6use std::env;
7use std::result::Result;
8use std::thread;
9use std::time::Duration;
10
11pub struct GateKeeperMemberListener<'a> {
12 nfc_device: GatekeeperReader<'a>,
13 http: reqwest::blocking::Client,
14
15 server_token: String,
17 endpoint: String,
19
20 just_scanned: bool,
22
23 route: &'static str,
25}
26
27pub enum FetchError {
28 NotFound,
29 ParseError,
30 NetworkError,
31 Unknown,
32}
33
34trait RealmTypeExt {
35 fn get_route(&self) -> &'static str;
36 fn env_name(&self) -> &'static str;
37 fn get_auth_key(&self) -> Vec<u8>;
38 fn get_read_key(&self) -> Vec<u8>;
39 fn get_desfire_signing_public_key(&self) -> Vec<u8>;
40 fn get_mobile_decryption_private_key(&self) -> Vec<u8>;
41 fn get_mobile_signing_private_key(&self) -> Vec<u8>;
42}
43
44impl RealmTypeExt for RealmType {
45 fn get_route(&self) -> &'static str {
46 match self {
47 Self::Door => "doors",
48 Self::Drink => "drink",
49 Self::MemberProjects => "projects",
50 }
51 }
52 fn env_name(&self) -> &'static str {
53 match self {
54 Self::Door => "DOORS",
55 Self::Drink => "DRINK",
56 Self::MemberProjects => "MEMBER_PROJECTS",
57 }
58 }
59 fn get_auth_key(&self) -> Vec<u8> {
60 env::var(format!("GK_REALM_{}_AUTH_KEY", self.env_name()))
61 .unwrap()
62 .into_bytes()
63 }
64 fn get_read_key(&self) -> Vec<u8> {
65 env::var(format!("GK_REALM_{}_READ_KEY", self.env_name()))
66 .unwrap()
67 .into_bytes()
68 }
69 fn get_desfire_signing_public_key(&self) -> Vec<u8> {
70 env::var(format!("GK_REALM_{}_PUBLIC_KEY", self.env_name()))
71 .unwrap()
72 .into_bytes()
73 }
74 fn get_mobile_decryption_private_key(&self) -> Vec<u8> {
75 env::var(format!(
76 "GK_REALM_{}_MOBILE_CRYPT_PRIVATE_KEY",
77 self.env_name()
78 ))
79 .unwrap()
80 .into_bytes()
81 }
82 fn get_mobile_signing_private_key(&self) -> Vec<u8> {
83 env::var(format!("GK_REALM_{}_MOBILE_PRIVATE_KEY", self.env_name()))
84 .unwrap()
85 .into_bytes()
86 }
87}
88
89impl<'a> GateKeeperMemberListener<'a> {
90 pub fn new(conn_str: String, realm_type: RealmType) -> Option<Self> {
91 let realm = Realm::new(
92 realm_type,
93 realm_type.get_auth_key(),
94 realm_type.get_read_key(),
95 &realm_type.get_desfire_signing_public_key(),
96 &realm_type.get_mobile_decryption_private_key(),
97 &realm_type.get_mobile_signing_private_key(),
98 None,
99 );
100
101 Some(GateKeeperMemberListener {
102 nfc_device: GatekeeperReader::new(conn_str, realm)?,
103 http: reqwest::blocking::Client::new(),
104
105 server_token: env::var("GK_SERVER_TOKEN").unwrap(),
106 just_scanned: false,
107 endpoint: env::var("GK_HTTP_ENDPOINT")
108 .unwrap_or_else(|_| "http://localhost:3000".to_string()),
109 route: realm_type.get_route(),
110 })
111 }
112
113 pub fn poll_for_tag(&mut self) -> Option<UndifferentiatedTag> {
114 let nearby_tags = self.nfc_device.get_nearby_tags();
115 if nearby_tags.is_empty() {
116 self.just_scanned = false;
117 }
118 if self.just_scanned {
119 thread::sleep(Duration::from_millis(250));
120 return None;
121 }
122 self.just_scanned = !nearby_tags.is_empty();
123 nearby_tags.into_iter().next()
124 }
125
126 pub fn poll_for_user(&mut self) -> Option<String> {
127 self.poll_for_tag().and_then(|tag| tag.authenticate().ok())
128 }
129
130 pub fn wait_for_user(&mut self) -> Option<String> {
131 loop {
132 if let Some(association) = self.poll_for_user() {
133 return Some(association);
134 }
135 }
136 }
137
138 pub fn fetch_user(&self, key: String) -> Result<Value, FetchError> {
139 match self
140 .http
141 .get(format!(
142 "{}/{}/by-key/{}",
143 self.endpoint.clone(),
144 self.route,
145 &key
146 ))
147 .header(AUTHORIZATION, self.server_token.clone())
148 .send()
149 {
150 Ok(res) => match res.status() {
151 StatusCode::OK => {
152 if let Ok(text) = res.text() {
153 if let Ok(value) = serde_json::from_str(&text) {
154 Ok(value)
155 } else {
156 Err(FetchError::ParseError)
157 }
158 } else {
159 Err(FetchError::ParseError)
160 }
161 }
162 StatusCode::NOT_FOUND => Err(FetchError::NotFound),
163 _ => Err(FetchError::Unknown),
164 },
165 Err(err) => {
166 println!("Error fetching data for key: {:?}", err);
167 Err(FetchError::NetworkError)
168 }
169 }
170 }
171}