1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
use std::fmt;
use std::ops::Deref;

use ref_cast::RefCast;

use crate::mtls::x509::X509Name;
use crate::mtls::oid;

/// An X.509 Distinguished Name (DN) found in a
/// [`Certificate`](crate::mtls::Certificate).
///
/// This type is a wrapper over [`X509Name`] with convenient methods and
/// complete documentation. Should the data exposed by the inherent methods not
/// suffice, this type derefs to [`X509Name`].
#[repr(transparent)]
#[derive(Debug, PartialEq, RefCast)]
pub struct Name<'a>(X509Name<'a>);

impl<'a> Name<'a> {
    /// Returns the _first_ UTF-8 _string_ common name, if any.
    ///
    /// Note that common names need not be UTF-8 strings, or strings at all.
    /// This method returns the first common name attribute that is.
    ///
    /// # Example
    ///
    /// ```rust
    /// # #[macro_use] extern crate rocket;
    /// use rocket::mtls::Certificate;
    ///
    /// #[get("/auth")]
    /// fn auth(cert: Certificate<'_>) {
    ///     if let Some(name) = cert.subject().common_name() {
    ///         println!("Hello, {}!", name);
    ///     }
    /// }
    /// ```
    pub fn common_name(&self) -> Option<&'a str> {
        self.common_names().next()
    }

    /// Returns an iterator over all of the UTF-8 _string_ common names in
    /// `self`.
    ///
    /// Note that common names need not be UTF-8 strings, or strings at all.
    /// This method filters the common names in `self` to those that are. Use
    /// the raw [`iter_common_name()`](#method.iter_common_name) to iterate over
    /// all value types.
    ///
    /// # Example
    ///
    /// ```rust
    /// # #[macro_use] extern crate rocket;
    /// use rocket::mtls::Certificate;
    ///
    /// #[get("/auth")]
    /// fn auth(cert: Certificate<'_>) {
    ///     for name in cert.issuer().common_names() {
    ///         println!("Issued by {}.", name);
    ///     }
    /// }
    /// ```
    pub fn common_names(&self) -> impl Iterator<Item = &'a str> + '_ {
        self.iter_by_oid(&oid::OID_X509_COMMON_NAME).filter_map(|n| n.as_str().ok())
    }

    /// Returns the _first_ UTF-8 _string_ email address, if any.
    ///
    /// Note that email addresses need not be UTF-8 strings, or strings at all.
    /// This method returns the first email address attribute that is.
    ///
    /// # Example
    ///
    /// ```rust
    /// # #[macro_use] extern crate rocket;
    /// use rocket::mtls::Certificate;
    ///
    /// #[get("/auth")]
    /// fn auth(cert: Certificate<'_>) {
    ///     if let Some(email) = cert.subject().email() {
    ///         println!("Hello, {}!", email);
    ///     }
    /// }
    /// ```
    pub fn email(&self) -> Option<&'a str> {
        self.emails().next()
    }

    /// Returns an iterator over all of the UTF-8 _string_ email addresses in
    /// `self`.
    ///
    /// Note that email addresses need not be UTF-8 strings, or strings at all.
    /// This method filters the email address in `self` to those that are. Use
    /// the raw [`iter_email()`](#method.iter_email) to iterate over all value
    /// types.
    ///
    /// # Example
    ///
    /// ```rust
    /// # #[macro_use] extern crate rocket;
    /// use rocket::mtls::Certificate;
    ///
    /// #[get("/auth")]
    /// fn auth(cert: Certificate<'_>) {
    ///     for email in cert.subject().emails() {
    ///         println!("Reach me at: {}", email);
    ///     }
    /// }
    /// ```
    pub fn emails(&self) -> impl Iterator<Item = &'a str> + '_ {
        self.iter_by_oid(&oid::OID_PKCS9_EMAIL_ADDRESS).filter_map(|n| n.as_str().ok())
    }

    /// Returns `true` if `self` has no data.
    ///
    /// When this is the case for a `subject()`, the subject data can be found
    /// in the `subjectAlt` [`extension`].
    ///
    /// [`extension`]: crate::mtls::Certificate::extensions()
    ///
    /// # Example
    ///
    /// ```rust
    /// # #[macro_use] extern crate rocket;
    /// use rocket::mtls::Certificate;
    ///
    /// #[get("/auth")]
    /// fn auth(cert: Certificate<'_>) {
    ///     let no_data = cert.subject().is_empty();
    /// }
    /// ```
    pub fn is_empty(&self) -> bool {
        self.0.as_raw().is_empty()
    }
}

impl<'a> Deref for Name<'a> {
    type Target = X509Name<'a>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl fmt::Display for Name<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.0.fmt(f)
    }
}