Skip to main content

rocketmq_auth/secret_provider/
environment.rs

1// Copyright 2023 The RocketMQ Rust Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::collections::BTreeMap;
16use std::fmt;
17
18use rocketmq_security_api::SecretAccess;
19use rocketmq_security_api::SecretMaterial;
20use rocketmq_security_api::SecretName;
21use rocketmq_security_api::SecretPersistence;
22use rocketmq_security_api::SecretProvider;
23use rocketmq_security_api::SecretProviderCapabilities;
24use rocketmq_security_api::SecretProviderId;
25use rocketmq_security_api::SecretVersion;
26use rocketmq_security_api::SecretVersioning;
27use rocketmq_security_api::SecurityOperation;
28use rocketmq_security_api::SecurityProviderError;
29use rocketmq_security_api::SecurityProviderFailure;
30use rocketmq_security_api::VersionedSecret;
31
32use crate::AuthFailureKind;
33use crate::AuthOperation;
34use crate::AuthServiceError;
35use crate::AuthServiceResult;
36
37/// Read-only development adapter that maps logical names to an explicit environment allowlist.
38pub struct EnvironmentSecretProvider {
39    id: SecretProviderId,
40    variables: BTreeMap<SecretName, String>,
41}
42
43impl EnvironmentSecretProvider {
44    /// Creates an adapter from an explicit logical-name to environment-variable mapping.
45    ///
46    /// # Errors
47    ///
48    /// Returns a redacted authentication-service error for invalid or duplicate mappings.
49    pub fn new(
50        id: SecretProviderId,
51        mappings: impl IntoIterator<Item = (SecretName, String)>,
52    ) -> AuthServiceResult<Self> {
53        let mut variables = BTreeMap::new();
54        for (name, variable) in mappings {
55            if variable.is_empty() || variable.contains(['\0', '=']) || variables.insert(name, variable).is_some() {
56                return Err(AuthServiceError::new(
57                    AuthOperation::LoadSecret,
58                    AuthFailureKind::InvalidConfiguration,
59                ));
60            }
61        }
62        if variables.is_empty() {
63            return Err(AuthServiceError::new(
64                AuthOperation::LoadSecret,
65                AuthFailureKind::InvalidConfiguration,
66            ));
67        }
68        Ok(Self { id, variables })
69    }
70}
71
72impl fmt::Debug for EnvironmentSecretProvider {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        f.debug_struct("EnvironmentSecretProvider")
75            .field("id", &self.id)
76            .field("mapped_name_count", &self.variables.len())
77            .finish()
78    }
79}
80
81impl SecretProvider for EnvironmentSecretProvider {
82    fn id(&self) -> &SecretProviderId {
83        &self.id
84    }
85
86    fn capabilities(&self) -> SecretProviderCapabilities {
87        SecretProviderCapabilities::new(
88            SecretAccess::ReadOnly,
89            SecretPersistence::ProcessEnvironment,
90            SecretVersioning::Unversioned,
91        )
92    }
93
94    fn read(&self, name: &SecretName) -> Result<VersionedSecret, SecurityProviderError> {
95        let variable = self.variables.get(name).ok_or_else(|| {
96            SecurityProviderError::new(SecurityProviderFailure::NotFound, SecurityOperation::ReadSecret)
97        })?;
98        let value = match std::env::var(variable) {
99            Ok(value) => value,
100            Err(std::env::VarError::NotPresent) => {
101                return Err(SecurityProviderError::new(
102                    SecurityProviderFailure::NotFound,
103                    SecurityOperation::ReadSecret,
104                ));
105            }
106            Err(std::env::VarError::NotUnicode(_)) => {
107                return Err(SecurityProviderError::new(
108                    SecurityProviderFailure::InvalidData,
109                    SecurityOperation::ReadSecret,
110                ));
111            }
112        };
113        let material = SecretMaterial::new(value.into_bytes()).map_err(|source| {
114            SecurityProviderError::caused_by(
115                SecurityProviderFailure::InvalidData,
116                SecurityOperation::ReadSecret,
117                source,
118            )
119        })?;
120        Ok(VersionedSecret::new(material, None))
121    }
122
123    fn write(
124        &self,
125        _name: &SecretName,
126        _material: SecretMaterial,
127        _expected_version: Option<SecretVersion>,
128    ) -> Result<SecretVersion, SecurityProviderError> {
129        Err(SecurityProviderError::new(
130            SecurityProviderFailure::Unsupported,
131            SecurityOperation::WriteSecret,
132        ))
133    }
134}