Skip to main content

epp_client/
login.rs

1use std::fmt::Debug;
2
3use serde::Serialize;
4
5use crate::{
6    common::{NoExtension, Options, ServiceExtension, Services, StringValue},
7    contact, domain, host,
8    request::{Command, Transaction, EPP_LANG, EPP_VERSION},
9};
10
11impl<'a> Transaction<NoExtension> for Login<'a> {}
12
13#[derive(Serialize, Debug, Eq, PartialEq)]
14/// Type corresponding to the &lt;login&gt; tag in an EPP XML login request
15pub struct Login<'a> {
16    /// The username to use for the login
17    #[serde(rename(serialize = "clID", deserialize = "clID"))]
18    username: StringValue<'a>,
19    /// The password to use for the login
20    #[serde(rename = "pw", default)]
21    password: StringValue<'a>,
22    /// A new password which should be set
23    #[serde(rename = "newPW", default, skip_serializing_if = "Option::is_none")]
24    new_password: Option<StringValue<'a>>,
25    /// Data under the <options> tag
26    options: Options<'a>,
27    /// Data under the <svcs> tag
28    #[serde(rename = "svcs")]
29    services: Services<'a>,
30}
31
32impl<'a> Login<'a> {
33    pub fn new(
34        username: &'a str,
35        password: &'a str,
36        new_password: Option<&'a str>,
37        ext_uris: Option<&'_ [&'a str]>,
38    ) -> Self {
39        let svc_ext = match ext_uris {
40            Some(uris) if !uris.is_empty() => Some(ServiceExtension {
41                ext_uris: Some(uris.iter().map(|&u| u.into()).collect()),
42            }),
43            _ => None,
44        };
45
46        Self {
47            username: username.into(),
48            password: password.into(),
49            new_password: new_password.map(Into::into),
50            options: Options {
51                version: EPP_VERSION.into(),
52                lang: EPP_LANG.into(),
53            },
54            services: Services {
55                obj_uris: vec![
56                    host::XMLNS.into(),
57                    contact::XMLNS.into(),
58                    domain::XMLNS.into(),
59                ],
60                svc_ext,
61            },
62        }
63    }
64
65    /// Sets the <options> tag data
66    pub fn options(&mut self, options: Options<'a>) {
67        self.options = options;
68    }
69
70    /// Sets the <svcs> tag data
71    pub fn services(&mut self, services: Services<'a>) {
72        self.services = services;
73    }
74}
75
76impl<'a> Command for Login<'a> {
77    type Response = ();
78    const COMMAND: &'static str = "login";
79}
80
81#[cfg(test)]
82mod tests {
83    use super::Login;
84    use crate::response::ResultCode;
85    use crate::tests::{assert_serialized, response_from_file, CLTRID, SUCCESS_MSG, SVTRID};
86
87    #[test]
88    fn command() {
89        let ext_uris = Some(&["http://schema.ispapi.net/epp/xml/keyvalue-1.0"][..]);
90        let object = Login::new("username", "password", Some("new-password"), ext_uris);
91        assert_serialized("request/login.xml", &object);
92    }
93
94    #[test]
95    fn command_no_extension() {
96        let object = Login::new("username", "password", Some("new-password"), None);
97        assert_serialized("request/login_no_extension.xml", &object);
98    }
99
100    #[test]
101    fn response() {
102        let object = response_from_file::<Login>("response/login.xml");
103        assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
104        assert_eq!(object.result.message, SUCCESS_MSG.into());
105        assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
106        assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
107    }
108}