1use super::{sync, GroupId, RemoteGroup, RemoteProject, RemoteUser};
2use binaryninjacore_sys::*;
3use std::env::VarError;
4use std::ffi::c_void;
5use std::ptr::NonNull;
6
7use crate::binary_view::BinaryView;
8use crate::database::Database;
9use crate::enterprise;
10use crate::progress::{NoProgressCallback, ProgressCallback};
11use crate::project::Project;
12use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
13use crate::secrets_provider::CoreSecretsProvider;
14use crate::settings::Settings;
15use crate::string::{BnString, IntoCStr};
16
17#[repr(transparent)]
18pub struct Remote {
19 pub(crate) handle: NonNull<BNRemote>,
20}
21
22impl Remote {
23 pub(crate) unsafe fn from_raw(handle: NonNull<BNRemote>) -> Self {
24 Self { handle }
25 }
26
27 pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNRemote>) -> Ref<Self> {
28 Ref::new(Self { handle })
29 }
30
31 pub fn new(name: &str, address: &str) -> Ref<Self> {
33 let name = name.to_cstr();
34 let address = address.to_cstr();
35 let result = unsafe { BNCollaborationCreateRemote(name.as_ptr(), address.as_ptr()) };
36 unsafe { Self::ref_from_raw(NonNull::new(result).unwrap()) }
37 }
38
39 pub fn get_for_local_database(database: &Database) -> Result<Option<Ref<Remote>>, ()> {
41 sync::get_remote_for_local_database(database)
42 }
43
44 pub fn get_for_binary_view(bv: &BinaryView) -> Result<Option<Ref<Remote>>, ()> {
46 sync::get_remote_for_binary_view(bv)
47 }
48
49 pub fn has_loaded_metadata(&self) -> bool {
51 unsafe { BNRemoteHasLoadedMetadata(self.handle.as_ptr()) }
52 }
53
54 pub fn unique_id(&self) -> Result<BnString, ()> {
56 if !self.has_loaded_metadata() {
57 self.load_metadata()?;
58 }
59 let result = unsafe { BNRemoteGetUniqueId(self.handle.as_ptr()) };
60 assert!(!result.is_null());
61 Ok(unsafe { BnString::from_raw(result) })
62 }
63
64 pub fn name(&self) -> String {
66 let result = unsafe { BNRemoteGetName(self.handle.as_ptr()) };
67 assert!(!result.is_null());
68 unsafe { BnString::into_string(result) }
69 }
70
71 pub fn address(&self) -> String {
73 let result = unsafe { BNRemoteGetAddress(self.handle.as_ptr()) };
74 assert!(!result.is_null());
75 unsafe { BnString::into_string(result) }
76 }
77
78 pub fn is_connected(&self) -> bool {
80 unsafe { BNRemoteIsConnected(self.handle.as_ptr()) }
81 }
82
83 pub fn username(&self) -> String {
85 let result = unsafe { BNRemoteGetUsername(self.handle.as_ptr()) };
86 assert!(!result.is_null());
87 unsafe { BnString::into_string(result) }
88 }
89
90 pub fn token(&self) -> String {
92 let result = unsafe { BNRemoteGetToken(self.handle.as_ptr()) };
93 assert!(!result.is_null());
94 unsafe { BnString::into_string(result) }
95 }
96
97 pub fn server_version(&self) -> Result<i32, ()> {
99 if !self.has_loaded_metadata() {
100 self.load_metadata()?;
101 }
102 Ok(unsafe { BNRemoteGetServerVersion(self.handle.as_ptr()) })
103 }
104
105 pub fn server_build_id(&self) -> Result<BnString, ()> {
107 if !self.has_loaded_metadata() {
108 self.load_metadata()?;
109 }
110 unsafe {
111 Ok(BnString::from_raw(BNRemoteGetServerBuildId(
112 self.handle.as_ptr(),
113 )))
114 }
115 }
116
117 pub fn auth_backends(&self) -> Result<(Array<BnString>, Array<BnString>), ()> {
120 if !self.has_loaded_metadata() {
121 self.load_metadata()?;
122 }
123
124 let mut backend_ids = std::ptr::null_mut();
125 let mut backend_names = std::ptr::null_mut();
126 let mut count = 0;
127 let success = unsafe {
128 BNRemoteGetAuthBackends(
129 self.handle.as_ptr(),
130 &mut backend_ids,
131 &mut backend_names,
132 &mut count,
133 )
134 };
135 success
136 .then(|| unsafe {
137 (
138 Array::new(backend_ids, count, ()),
139 Array::new(backend_names, count, ()),
140 )
141 })
142 .ok_or(())
143 }
144
145 pub fn is_admin(&self) -> Result<bool, ()> {
147 if !self.has_pulled_users() {
148 self.pull_users()?;
149 }
150 Ok(unsafe { BNRemoteIsAdmin(self.handle.as_ptr()) })
151 }
152
153 pub fn is_enterprise(&self) -> Result<bool, ()> {
155 if !self.has_loaded_metadata() {
156 self.load_metadata()?;
157 }
158 Ok(unsafe { BNRemoteIsEnterprise(self.handle.as_ptr()) })
159 }
160
161 pub fn load_metadata(&self) -> Result<(), ()> {
163 let success = unsafe { BNRemoteLoadMetadata(self.handle.as_ptr()) };
164 success.then_some(()).ok_or(())
165 }
166
167 pub fn request_authentication_token(&self, username: &str, password: &str) -> Option<String> {
169 let username = username.to_cstr();
170 let password = password.to_cstr();
171 let token = unsafe {
172 BNRemoteRequestAuthenticationToken(
173 self.handle.as_ptr(),
174 username.as_ptr(),
175 password.as_ptr(),
176 )
177 };
178 if token.is_null() {
179 None
180 } else {
181 Some(unsafe { BnString::into_string(token) })
182 }
183 }
184
185 pub fn connect(&self) -> Result<(), ()> {
193 if self.is_enterprise()? && enterprise::is_server_authenticated() {
194 self.connect_with_opts(ConnectionOptions::from_enterprise()?)
195 } else {
196 match ConnectionOptions::from_env_variables() {
198 Ok(connection_opts) => self.connect_with_opts(connection_opts),
199 Err(_) => {
200 let secrets_connection_opts =
202 ConnectionOptions::from_secrets_provider(&self.address())?;
203 self.connect_with_opts(secrets_connection_opts)
204 }
205 }
206 }
207 }
208
209 pub fn connect_with_opts(&self, options: ConnectionOptions) -> Result<(), ()> {
211 if !self.has_loaded_metadata() {
213 self.load_metadata()?;
214 }
215 let token = match options.token {
216 Some(token) => token,
217 None => {
218 let password = options
220 .password
221 .expect("No password or token for connection!");
222 let token = self.request_authentication_token(&options.username, &password);
223 token.unwrap().to_string()
225 }
226 };
227 let username = options.username.to_cstr();
228 let token = token.to_cstr();
229 let success =
230 unsafe { BNRemoteConnect(self.handle.as_ptr(), username.as_ptr(), token.as_ptr()) };
231 success.then_some(()).ok_or(())
232 }
233
234 pub fn disconnect(&self) -> Result<(), ()> {
240 let success = unsafe { BNRemoteDisconnect(self.handle.as_ptr()) };
241 success.then_some(()).ok_or(())
242 }
243
244 pub fn has_pulled_projects(&self) -> bool {
246 unsafe { BNRemoteHasPulledProjects(self.handle.as_ptr()) }
247 }
248
249 pub fn has_pulled_groups(&self) -> bool {
251 unsafe { BNRemoteHasPulledGroups(self.handle.as_ptr()) }
252 }
253
254 pub fn has_pulled_users(&self) -> bool {
256 unsafe { BNRemoteHasPulledUsers(self.handle.as_ptr()) }
257 }
258
259 pub fn projects(&self) -> Result<Array<RemoteProject>, ()> {
263 if !self.has_pulled_projects() {
264 self.pull_projects()?;
265 }
266
267 let mut count = 0;
268 let value = unsafe { BNRemoteGetProjects(self.handle.as_ptr(), &mut count) };
269 if value.is_null() {
270 return Err(());
271 }
272 Ok(unsafe { Array::new(value, count, ()) })
273 }
274
275 pub fn get_project_by_id(&self, id: &str) -> Result<Option<Ref<RemoteProject>>, ()> {
279 if !self.has_pulled_projects() {
280 self.pull_projects()?;
281 }
282
283 let id = id.to_cstr();
284 let value = unsafe { BNRemoteGetProjectById(self.handle.as_ptr(), id.as_ptr()) };
285 Ok(NonNull::new(value).map(|handle| unsafe { RemoteProject::ref_from_raw(handle) }))
286 }
287
288 pub fn get_project_by_name(&self, name: &str) -> Result<Option<Ref<RemoteProject>>, ()> {
292 if !self.has_pulled_projects() {
293 self.pull_projects()?;
294 }
295
296 let name = name.to_cstr();
297 let value = unsafe { BNRemoteGetProjectByName(self.handle.as_ptr(), name.as_ptr()) };
298 Ok(NonNull::new(value).map(|handle| unsafe { RemoteProject::ref_from_raw(handle) }))
299 }
300
301 pub fn pull_projects(&self) -> Result<(), ()> {
303 self.pull_projects_with_progress(NoProgressCallback)
304 }
305
306 pub fn pull_projects_with_progress<F: ProgressCallback>(
312 &self,
313 mut progress: F,
314 ) -> Result<(), ()> {
315 let success = unsafe {
316 BNRemotePullProjects(
317 self.handle.as_ptr(),
318 Some(F::cb_progress_callback),
319 &mut progress as *mut F as *mut c_void,
320 )
321 };
322 success.then_some(()).ok_or(())
323 }
324
325 pub fn create_project(&self, name: &str, description: &str) -> Result<Ref<RemoteProject>, ()> {
332 if !self.has_pulled_projects() {
336 self.pull_projects()?;
337 }
338 let name = name.to_cstr();
339 let description = description.to_cstr();
340 let value = unsafe {
341 BNRemoteCreateProject(self.handle.as_ptr(), name.as_ptr(), description.as_ptr())
342 };
343 NonNull::new(value)
344 .map(|handle| unsafe { RemoteProject::ref_from_raw(handle) })
345 .ok_or(())
346 }
347
348 pub fn import_local_project(&self, project: &Project) -> Option<Ref<RemoteProject>> {
350 self.import_local_project_with_progress(project, NoProgressCallback)
351 }
352
353 pub fn import_local_project_with_progress<P: ProgressCallback>(
355 &self,
356 project: &Project,
357 mut progress: P,
358 ) -> Option<Ref<RemoteProject>> {
359 let value = unsafe {
360 BNRemoteImportLocalProject(
361 self.handle.as_ptr(),
362 project.handle.as_ptr(),
363 Some(P::cb_progress_callback),
364 &mut progress as *mut P as *mut c_void,
365 )
366 };
367 NonNull::new(value).map(|handle| unsafe { RemoteProject::ref_from_raw(handle) })
368 }
369
370 pub fn push_project<I>(&self, project: &RemoteProject, extra_fields: I) -> Result<(), ()>
377 where
378 I: IntoIterator<Item = (String, String)>,
379 {
380 let (keys, values): (Vec<_>, Vec<_>) = extra_fields
381 .into_iter()
382 .map(|(k, v)| (k.to_cstr(), v.to_cstr()))
383 .unzip();
384 let mut keys_raw = keys.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
385 let mut values_raw = values.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
386
387 let success = unsafe {
388 BNRemotePushProject(
389 self.handle.as_ptr(),
390 project.handle.as_ptr(),
391 keys_raw.as_mut_ptr(),
392 values_raw.as_mut_ptr(),
393 keys_raw.len(),
394 )
395 };
396 success.then_some(()).ok_or(())
397 }
398
399 pub fn delete_project(&self, project: &RemoteProject) -> Result<(), ()> {
401 let success =
402 unsafe { BNRemoteDeleteProject(self.handle.as_ptr(), project.handle.as_ptr()) };
403 success.then_some(()).ok_or(())
404 }
405
406 pub fn groups(&self) -> Result<Array<RemoteGroup>, ()> {
411 if !self.has_pulled_groups() {
412 self.pull_groups()?;
413 }
414
415 let mut count = 0;
416 let value = unsafe { BNRemoteGetGroups(self.handle.as_ptr(), &mut count) };
417 if value.is_null() {
418 return Err(());
419 }
420 Ok(unsafe { Array::new(value, count, ()) })
421 }
422
423 pub fn get_group_by_id(&self, id: GroupId) -> Result<Option<Ref<RemoteGroup>>, ()> {
428 if !self.has_pulled_groups() {
429 self.pull_groups()?;
430 }
431
432 let value = unsafe { BNRemoteGetGroupById(self.handle.as_ptr(), id.0) };
433 Ok(NonNull::new(value).map(|handle| unsafe { RemoteGroup::ref_from_raw(handle) }))
434 }
435
436 pub fn get_group_by_name(&self, name: &str) -> Result<Option<Ref<RemoteGroup>>, ()> {
441 if !self.has_pulled_groups() {
442 self.pull_groups()?;
443 }
444
445 let name = name.to_cstr();
446 let value = unsafe { BNRemoteGetGroupByName(self.handle.as_ptr(), name.as_ptr()) };
447
448 Ok(NonNull::new(value).map(|handle| unsafe { RemoteGroup::ref_from_raw(handle) }))
449 }
450
451 pub fn search_groups(&self, prefix: &str) -> Result<(Array<GroupId>, Array<BnString>), ()> {
457 let prefix = prefix.to_cstr();
458 let mut count = 0;
459 let mut group_ids = std::ptr::null_mut();
460 let mut group_names = std::ptr::null_mut();
461
462 let success = unsafe {
463 BNRemoteSearchGroups(
464 self.handle.as_ptr(),
465 prefix.as_ptr(),
466 &mut group_ids,
467 &mut group_names,
468 &mut count,
469 )
470 };
471 if !success {
472 return Err(());
473 }
474 Ok(unsafe {
475 (
476 Array::new(group_ids, count, ()),
477 Array::new(group_names, count, ()),
478 )
479 })
480 }
481
482 pub fn pull_groups(&self) -> Result<(), ()> {
485 self.pull_groups_with_progress(NoProgressCallback)
486 }
487
488 pub fn pull_groups_with_progress<F: ProgressCallback>(
495 &self,
496 mut progress: F,
497 ) -> Result<(), ()> {
498 let success = unsafe {
499 BNRemotePullGroups(
500 self.handle.as_ptr(),
501 Some(F::cb_progress_callback),
502 &mut progress as *mut F as *mut c_void,
503 )
504 };
505 success.then_some(()).ok_or(())
506 }
507
508 pub fn create_group<I>(&self, name: &str, usernames: I) -> Result<Ref<RemoteGroup>, ()>
516 where
517 I: IntoIterator<Item = String>,
518 {
519 let name = name.to_cstr();
520 let usernames: Vec<_> = usernames.into_iter().map(|s| s.to_cstr()).collect();
521 let mut username_ptrs: Vec<_> = usernames.iter().map(|s| s.as_ptr()).collect();
522
523 let value = unsafe {
524 BNRemoteCreateGroup(
525 self.handle.as_ptr(),
526 name.as_ptr(),
527 username_ptrs.as_mut_ptr(),
528 username_ptrs.len(),
529 )
530 };
531 NonNull::new(value)
532 .map(|handle| unsafe { RemoteGroup::ref_from_raw(handle) })
533 .ok_or(())
534 }
535
536 pub fn push_group<I>(&self, group: &RemoteGroup, extra_fields: I) -> Result<(), ()>
544 where
545 I: IntoIterator<Item = (String, String)>,
546 {
547 let (keys, values): (Vec<_>, Vec<_>) = extra_fields
548 .into_iter()
549 .map(|(k, v)| (k.to_cstr(), v.to_cstr()))
550 .unzip();
551 let mut keys_raw: Vec<_> = keys.iter().map(|s| s.as_ptr()).collect();
552 let mut values_raw: Vec<_> = values.iter().map(|s| s.as_ptr()).collect();
553
554 let success = unsafe {
555 BNRemotePushGroup(
556 self.handle.as_ptr(),
557 group.handle.as_ptr(),
558 keys_raw.as_mut_ptr(),
559 values_raw.as_mut_ptr(),
560 keys.len(),
561 )
562 };
563 success.then_some(()).ok_or(())
564 }
565
566 pub fn delete_group(&self, group: &RemoteGroup) -> Result<(), ()> {
574 let success = unsafe { BNRemoteDeleteGroup(self.handle.as_ptr(), group.handle.as_ptr()) };
575 success.then_some(()).ok_or(())
576 }
577
578 pub fn users(&self) -> Result<Array<RemoteUser>, ()> {
584 if !self.has_pulled_users() {
585 self.pull_users()?;
586 }
587 let mut count = 0;
588 let value = unsafe { BNRemoteGetUsers(self.handle.as_ptr(), &mut count) };
589 if value.is_null() {
590 return Err(());
591 }
592 Ok(unsafe { Array::new(value, count, ()) })
593 }
594
595 pub fn get_user_by_id(&self, id: &str) -> Result<Option<Ref<RemoteUser>>, ()> {
605 if !self.has_pulled_users() {
606 self.pull_users()?;
607 }
608 let id = id.to_cstr();
609 let value = unsafe { BNRemoteGetUserById(self.handle.as_ptr(), id.as_ptr()) };
610 Ok(NonNull::new(value).map(|handle| unsafe { RemoteUser::ref_from_raw(handle) }))
611 }
612
613 pub fn get_user_by_username(&self, username: &str) -> Result<Option<Ref<RemoteUser>>, ()> {
623 if !self.has_pulled_users() {
624 self.pull_users()?;
625 }
626 let username = username.to_cstr();
627 let value = unsafe { BNRemoteGetUserByUsername(self.handle.as_ptr(), username.as_ptr()) };
628 Ok(NonNull::new(value).map(|handle| unsafe { RemoteUser::ref_from_raw(handle) }))
629 }
630
631 pub fn current_user(&self) -> Result<Option<Ref<RemoteUser>>, ()> {
637 if !self.has_pulled_users() {
638 self.pull_users()?;
639 }
640 let value = unsafe { BNRemoteGetCurrentUser(self.handle.as_ptr()) };
641 Ok(NonNull::new(value).map(|handle| unsafe { RemoteUser::ref_from_raw(handle) }))
642 }
643
644 pub fn search_users(&self, prefix: &str) -> Result<(Array<BnString>, Array<BnString>), ()> {
650 let prefix = prefix.to_cstr();
651 let mut count = 0;
652 let mut user_ids = std::ptr::null_mut();
653 let mut usernames = std::ptr::null_mut();
654 let success = unsafe {
655 BNRemoteSearchUsers(
656 self.handle.as_ptr(),
657 prefix.as_ptr(),
658 &mut user_ids,
659 &mut usernames,
660 &mut count,
661 )
662 };
663
664 if !success {
665 return Err(());
666 }
667 assert!(!user_ids.is_null());
668 assert!(!usernames.is_null());
669 Ok(unsafe {
670 (
671 Array::new(user_ids, count, ()),
672 Array::new(usernames, count, ()),
673 )
674 })
675 }
676
677 pub fn pull_users(&self) -> Result<(), ()> {
682 self.pull_users_with_progress(NoProgressCallback)
683 }
684
685 pub fn pull_users_with_progress<P: ProgressCallback>(&self, mut progress: P) -> Result<(), ()> {
694 let success = unsafe {
695 BNRemotePullUsers(
696 self.handle.as_ptr(),
697 Some(P::cb_progress_callback),
698 &mut progress as *mut P as *mut c_void,
699 )
700 };
701 success.then_some(()).ok_or(())
702 }
703
704 pub fn create_user(
712 &self,
713 username: &str,
714 email: &str,
715 is_active: bool,
716 password: &str,
717 group_ids: &[u64],
718 user_permission_ids: &[u64],
719 ) -> Result<Ref<RemoteUser>, ()> {
720 let username = username.to_cstr();
721 let email = email.to_cstr();
722 let password = password.to_cstr();
723
724 let value = unsafe {
725 BNRemoteCreateUser(
726 self.handle.as_ptr(),
727 username.as_ptr(),
728 email.as_ptr(),
729 is_active,
730 password.as_ptr(),
731 group_ids.as_ptr(),
732 group_ids.len(),
733 user_permission_ids.as_ptr(),
734 user_permission_ids.len(),
735 )
736 };
737 NonNull::new(value)
738 .map(|handle| unsafe { RemoteUser::ref_from_raw(handle) })
739 .ok_or(())
740 }
741
742 pub fn push_user<I>(&self, user: &RemoteUser, extra_fields: I) -> Result<(), ()>
751 where
752 I: IntoIterator<Item = (String, String)>,
753 {
754 let (keys, values): (Vec<_>, Vec<_>) = extra_fields
755 .into_iter()
756 .map(|(k, v)| (k.to_cstr(), v.to_cstr()))
757 .unzip();
758 let mut keys_raw: Vec<_> = keys.iter().map(|s| s.as_ptr()).collect();
759 let mut values_raw: Vec<_> = values.iter().map(|s| s.as_ptr()).collect();
760 let success = unsafe {
761 BNRemotePushUser(
762 self.handle.as_ptr(),
763 user.handle.as_ptr(),
764 keys_raw.as_mut_ptr(),
765 values_raw.as_mut_ptr(),
766 keys_raw.len(),
767 )
768 };
769 success.then_some(()).ok_or(())
770 }
771
772 }
778
779impl PartialEq for Remote {
780 fn eq(&self, other: &Self) -> bool {
781 if !self.has_loaded_metadata() || other.has_loaded_metadata() {
783 self.address() == other.address()
784 } else if let Some((slf, oth)) = self.unique_id().ok().zip(other.unique_id().ok()) {
785 slf == oth
786 } else {
787 self.address() == other.address()
789 }
790 }
791}
792impl Eq for Remote {}
793
794impl ToOwned for Remote {
795 type Owned = Ref<Self>;
796
797 fn to_owned(&self) -> Self::Owned {
798 unsafe { RefCountable::inc_ref(self) }
799 }
800}
801
802unsafe impl RefCountable for Remote {
803 unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
804 Ref::new(Self {
805 handle: NonNull::new(BNNewRemoteReference(handle.handle.as_ptr())).unwrap(),
806 })
807 }
808
809 unsafe fn dec_ref(handle: &Self) {
810 BNFreeRemote(handle.handle.as_ptr());
811 }
812}
813
814impl CoreArrayProvider for Remote {
815 type Raw = *mut BNRemote;
816 type Context = ();
817 type Wrapped<'a> = Guard<'a, Self>;
818}
819
820unsafe impl CoreArrayProviderInner for Remote {
821 unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
822 BNFreeRemoteList(raw, count)
823 }
824
825 unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
826 let raw_ptr = NonNull::new(*raw).unwrap();
827 Guard::new(Self::from_raw(raw_ptr), context)
828 }
829}
830
831#[derive(Debug, Clone, PartialEq, Eq, Hash)]
832pub struct ConnectionOptions {
833 pub username: String,
834 pub password: Option<String>,
836 pub token: Option<String>,
840}
841
842impl ConnectionOptions {
843 pub fn new_with_token(username: String, token: String) -> Self {
844 Self {
845 username,
846 token: Some(token),
847 password: None,
848 }
849 }
850
851 pub fn new_with_password(username: String, password: String) -> Self {
852 Self {
853 username,
854 token: None,
855 password: Some(password),
856 }
857 }
858
859 pub fn with_token(self, token: String) -> Self {
860 Self {
861 token: Some(token),
862 ..self
863 }
864 }
865
866 pub fn with_password(self, token: String) -> Self {
867 Self {
868 token: Some(token),
869 ..self
870 }
871 }
872
873 pub fn from_enterprise() -> Result<Self, ()> {
874 let username = enterprise::server_username();
876 let token = enterprise::server_token();
877 Ok(Self::new_with_token(username, token))
878 }
879
880 pub fn from_secrets_provider(address: &str) -> Result<Self, ()> {
884 let secrets_provider_name = Settings::new().get_string("enterprise.secretsProvider");
885 let provider = CoreSecretsProvider::by_name(&secrets_provider_name).ok_or(())?;
886 let cred_data_str = provider.get_data(address);
887 if cred_data_str.is_empty() {
888 return Err(());
889 }
890 let cred_data: serde_json::Value = serde_json::from_str(&cred_data_str).map_err(|_| ())?;
891 let username = cred_data["username"].as_str().ok_or(())?;
892 let token = cred_data["token"].as_str().ok_or(())?;
893 Ok(Self::new_with_token(
894 username.to_string(),
895 token.to_string(),
896 ))
897 }
898
899 pub fn from_env_variables() -> Result<Self, VarError> {
900 let username = std::env::var("BN_ENTERPRISE_USERNAME")?;
901 let password = std::env::var("BN_ENTERPRISE_PASSWORD")?;
902 Ok(ConnectionOptions::new_with_password(username, password))
903 }
904}