1use serde_json::{Value, json};
2use super::urls::OPENID_URL;
3use minreq::{post, Error};
4use crate::types::Token;
5
6#[derive(Debug)]
7pub struct OpenID {
8 server_url: String,
9 client_id: String,
10 client_secret: String,
11 pub token: Token,
12 roles: Vec<String>,
13}
14
15impl OpenID {
16 fn get_realm_roles(token: &Token) -> Vec<String>{
17 match token {
18 Token::Introspect(e) => {
19 match e.get("realm_access") {
20 Some(ee) => {
21 let token_roles = ee.get("roles")
22 .unwrap()
23 .as_array()
24 .unwrap();
25
26 token_roles.iter()
27 .map(|i| i.to_string().trim_matches('\"').to_string())
28 .collect::<Vec<String>>()
29 },
30 None => vec![],
31 }
32 },
33 _ => vec![]
34 }
35 }
36
37 pub fn introspect(server_url: &str, realm_name: &str, client_id: &str, client_secret: &str, access_token: &str) -> Result<Self, Error> {
38 let path = OPENID_URL.introspect.replace("{realm-name}", realm_name);
39
40 let url = format!(
41 "{}/{}",
42 server_url,
43 path,
44 );
45
46 let res = post(url)
47 .with_header("Content-Type", "application/x-www-form-urlencoded")
48 .with_body(
49 format!(
50 "client_id={}&client_secret={}&token={}",
51 client_id,
52 client_secret,
53 access_token,
54 )
55 ).send();
56
57 match res {
58 Ok(e) => {
59 if e.status_code != 200 {
60 return Err(Error::Other("Unauthorized"));
61 }
62
63 let token = Token::Introspect(e.clone().json().unwrap());
64
65 let realm_roles = Self::get_realm_roles(&token);
66
67 Ok(Self {
68 server_url: server_url.to_string(),
69 client_id: client_id.to_string(),
70 client_secret: client_secret.to_string(),
71 token: token,
72 roles: realm_roles,
73 })
74 },
75 Err(e) => Err(e),
76 }
77 }
78
79 pub fn login_with_client(server_url: &str, realm_name: &str, client_id: &str, client_secret: &str) -> Result<Self, Error> {
80 let path = OPENID_URL.token.replace("{realm-name}", realm_name);
81
82 let url = format!(
83 "{}/{}",
84 server_url,
85 path,
86 );
87
88 let res = post(url)
89 .with_header("Content-Type", "application/x-www-form-urlencoded")
90 .with_body(
91 format!(
92 "client_id={}&client_secret={}&grant_type=client_credentials",
93 client_id,
94 client_secret,
95 )
96 ).send();
97
98 match res {
99 Ok(e) => {
100 if e.status_code != 200 {
101 return Err(Error::Other("Unauthorized"));
102 }
103
104 let token = Token::Client(e.clone().json().unwrap());
105
106 Ok(Self {
107 server_url: server_url.to_string(),
108 client_id: client_id.to_string(),
109 client_secret: client_secret.to_string(),
110 token: token,
111 roles: vec![],
112 })
113 },
114 Err(e) => Err(e),
115 }
116 }
117
118 pub fn login_with_password(server_url: &str, realm_name: &str, client_id: &str, client_secret: &str, username: &str, password: &str) -> Result<Self, Error> {
119 let path = OPENID_URL.token.replace("{realm-name}", realm_name);
120
121 let url = format!(
122 "{}/{}",
123 server_url,
124 path,
125 );
126
127 let res = post(url)
128 .with_header("Content-Type", "application/x-www-form-urlencoded")
129 .with_body(
130 format!(
131 "scope=openid&client_id={}&client_secret={}&grant_type=password&username={}&password={}",
132 client_id,
133 client_secret,
134 username,
135 password,
136 )
137 ).send();
138
139 match res {
140 Ok(e) => {
141 if e.status_code != 200 {
142 return Err(Error::Other("Unauthorized"));
143 }
144
145 let token = Token::Password(e.clone().json().unwrap());
146
147 Ok(Self {
148 server_url: server_url.to_string(),
149 client_id: client_id.to_string(),
150 client_secret: client_secret.to_string(),
151 token: token,
152 roles: vec![],
153 })
154 },
155 Err(e) => Err(e),
156 }
157 }
158
159 pub fn logout(server_url: &str, realm_name: &str, client_id: &str, client_secret: &str, refresh_token: &str) -> Result<(), Error> {
160 let path = OPENID_URL.logout.replace("{realm-name}", realm_name);
161
162 let url = format!(
163 "{}/{}",
164 server_url,
165 path,
166 );
167
168 let res = post(url)
169 .with_header("Content-Type", "application/x-www-form-urlencoded")
170 .with_body(
171 format!(
172 "client_id={}&client_secret={}&refresh_token={}",
173 client_id,
174 client_secret,
175 refresh_token,
176 )
177 ).send();
178
179 match res {
180 Ok(e) => {
181 if e.status_code != 204 {
182 return Err(Error::Other("Unauthorized"));
183 }
184
185 Ok(())
186 },
187 Err(e) => Err(e),
188 }
189 }
190
191 pub fn get_token_type(&self) -> String {
192 match &self.token {
193 Token::Client(token) => token.token_type.to_string(),
194 Token::Password(token) => token.token_type.to_string(),
195 _ => "".to_string(),
196 }
197 }
198
199 pub fn get_access_token(&self) -> String {
200 match &self.token {
201 Token::Client(token) => token.access_token.to_string(),
202 Token::Password(token) => token.access_token.to_string(),
203 _ => "".to_string(),
204 }
205 }
206
207 pub fn get_refresh_token(&self) -> Option<String> {
208 match &self.token {
209 Token::Client(_) => None,
210 Token::Password(token) => Some(token.refresh_token.to_string()),
211 _ => None,
212 }
213 }
214
215 pub fn get_expires_in(&self) -> u64 {
216 match &self.token {
217 Token::Client(token) => token.expires_in,
218 Token::Password(token) => token.expires_in,
219 _ => 0,
220 }
221 }
222
223 pub fn get_refresh_expires_in(&self) -> u64 {
224 match &self.token {
225 Token::Client(token) => token.refresh_expires_in,
226 Token::Password(token) => token.refresh_expires_in,
227 _ => 0,
228 }
229 }
230
231 pub fn get_scopes(&self) -> Vec<&str> {
232 match &self.token {
233 Token::Client(token) => token.scope.split(' ').collect::<Vec<&str>>(),
234 Token::Password(token) => token.scope.split(' ').collect::<Vec<&str>>(),
235 _ => vec![],
236 }
237 }
238
239 pub fn get_roles(&self) -> &Vec<String> {
240 &self.roles
241 }
242
243 pub fn has_any_roles(&self, roles: &[&str]) -> bool {
244 for role in &self.roles {
245 if roles.contains(&role.as_str()) {
246 return true;
247 }
248 }
249
250 false
251 }
252
253 pub fn has_all_roles(&self, roles: &[&str]) -> bool {
254 let roles_count = roles.len();
255
256 let mut roles_found: usize = 0;
257
258 for role in &self.roles {
259 if roles.contains(&role.as_str()) {
260 roles_found += 1;
261 }
262 }
263
264 roles_count == roles_found
265 }
266
267 pub fn get_decoded_token(&self) -> Value {
268 match &self.token {
269 Token::Introspect(e) => e.clone(),
270 _ => json!({}),
271 }
272 }
273}