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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
use std::path::PathBuf;

use anyhow::{Context as _, Result};

/// The type of a registry
#[derive(Debug, Default, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum RegistryType {
    /// OCI registry
    #[default]
    Oci,
}

/// The authentication settings for a registry
#[derive(Debug, Default, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum RegistryAuth {
    /// HTTP Basic authentication (username and password)
    Basic(String, String),
    /// token authentication
    Token(String),
    /// No authentication
    #[default]
    Anonymous,
}

impl From<(Option<String>, Option<String>)> for RegistryAuth {
    fn from((maybe_username, maybe_password): (Option<String>, Option<String>)) -> Self {
        match (maybe_username, maybe_password) {
            (Some(username), Some(password)) => Self::Basic(username, password),
            _ => Self::Anonymous,
        }
    }
}

#[cfg(feature = "oci")]
impl From<&RegistryAuth> for oci_client::secrets::RegistryAuth {
    fn from(auth: &crate::RegistryAuth) -> Self {
        match auth {
            crate::RegistryAuth::Basic(username, password) => {
                Self::Basic(username.clone(), password.clone())
            }
            _ => Self::Anonymous,
        }
    }
}

#[cfg(feature = "oci")]
impl From<RegistryAuth> for oci_client::secrets::RegistryAuth {
    fn from(auth: crate::RegistryAuth) -> Self {
        match auth {
            crate::RegistryAuth::Basic(username, password) => Self::Basic(username, password),
            _ => Self::Anonymous,
        }
    }
}

/// Credentials for a registry containing wasmCloud artifacts
#[derive(Debug, Default)]
#[non_exhaustive]
pub struct RegistryConfig {
    /// The type of the registry (only OCI is supported at this time)
    pub(crate) reg_type: RegistryType,
    /// The auth settings for the registry
    pub(crate) auth: RegistryAuth,
    /// Whether or not to allow downloading artifacts with the tag `latest`. Only valid for OCI registries
    pub(crate) allow_latest: bool,
    /// Whether or not to allow downloading artifacts over HTTP
    pub(crate) allow_insecure: bool,
    /// Additional CAs to include in the OCI client configuration
    pub(crate) additional_ca_paths: Vec<PathBuf>,
}

/// Builder for constructing a [`RegistryConfig`]
///
/// While `reg_type` and `auth` are not explicitly required, they must be provided, otherwise
/// building will fail.
#[derive(Debug, Clone, Default)]
#[allow(unused)]
pub struct RegistryConfigBuilder {
    reg_type: Option<RegistryType>,
    auth: Option<RegistryAuth>,
    allow_latest: Option<bool>,
    allow_insecure: Option<bool>,
    additional_ca_paths: Option<Vec<PathBuf>>,
}

impl RegistryConfigBuilder {
    pub fn reg_type(mut self, rt: RegistryType) -> Self {
        self.reg_type = Some(rt);
        self
    }

    pub fn auth(mut self, ra: RegistryAuth) -> Self {
        self.auth = Some(ra);
        self
    }

    pub fn allow_latest(mut self, latest: bool) -> Self {
        self.allow_latest = Some(latest);
        self
    }

    pub fn allow_insecure(mut self, insecure: bool) -> Self {
        self.allow_insecure = Some(insecure);
        self
    }

    pub fn additional_ca_paths(mut self, acp: impl IntoIterator<Item = PathBuf>) -> Self {
        self.additional_ca_paths = Some(acp.into_iter().collect::<Vec<PathBuf>>());
        self
    }

    pub fn build(self) -> Result<RegistryConfig> {
        let allow_insecure = self.allow_insecure.unwrap_or_default();
        Ok(RegistryConfig {
            reg_type: self.reg_type.context("missing registry type")?,
            auth: if allow_insecure {
                self.auth.unwrap_or_default()
            } else {
                self.auth.context("missing registry auth")?
            },
            allow_latest: self.allow_insecure.unwrap_or_default(),
            allow_insecure,
            additional_ca_paths: self.additional_ca_paths.unwrap_or_default(),
        })
    }
}

impl RegistryConfig {
    pub fn builder() -> RegistryConfigBuilder {
        RegistryConfigBuilder::default()
    }

    pub fn reg_type(&self) -> &RegistryType {
        &self.reg_type
    }

    pub fn auth(&self) -> &RegistryAuth {
        &self.auth
    }

    pub fn set_auth(&mut self, value: RegistryAuth) {
        self.auth = value;
    }

    pub fn allow_latest(&self) -> bool {
        self.allow_latest
    }

    pub fn set_allow_latest(&mut self, value: bool) {
        self.allow_latest = value;
    }

    pub fn allow_insecure(&self) -> bool {
        self.allow_insecure
    }

    pub fn set_allow_insecure(&mut self, value: bool) {
        self.allow_insecure = value;
    }

    pub fn additional_ca_paths(&self) -> &Vec<PathBuf> {
        &self.additional_ca_paths
    }

    pub fn set_additional_ca_paths(&mut self, value: Vec<PathBuf>) {
        self.additional_ca_paths = value;
    }
}