Coverage Report

Created: 2026-10-26 13:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
common/cpp/obj-client/auth/obj_credential.cpp
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#include "cpp/obj-client/auth/obj_credential.h"
19
20
#include <array>
21
#include <functional>
22
#include <utility>
23
24
namespace doris {
25
namespace {
26
27
struct CredentialParseResult {
28
    CredentialConfig credential {};
29
    std::optional<std::string> error = std::nullopt;
30
};
31
32
// Adapt provider-owned parsers to the generic registry. A provider parser
33
// returns no credential when none of its properties are present and an error
34
// for a partially configured credential.
35
1.66k
CredentialParseResult parse_gcp_properties(const ObjCredentialPropertyGetter& get_property) {
36
1.66k
    auto result = parse_gcp_credential_properties(get_property);
37
1.66k
    if (result.error.has_value()) {
38
5
        return {.error = std::move(result.error)};
39
5
    }
40
1.66k
    if (!result.credential.has_value()) {
41
1.64k
        return {};
42
1.64k
    }
43
15
    return {.credential = std::move(*result.credential)};
44
1.66k
}
45
46
using CredentialPropertyParser =
47
        CredentialParseResult (*)(const ObjCredentialPropertyGetter& get_property);
48
49
// Register exactly one parser per non-monostate CredentialConfig alternative.
50
// The loop below also rejects properties from multiple credential providers.
51
// Add parse_oss_credential_properties/parse_azure_credential_properties here
52
// when those passwordless modes are introduced.
53
constexpr std::array CREDENTIAL_PROPERTY_PARSERS {
54
        CredentialPropertyParser {parse_gcp_properties},
55
};
56
57
static_assert(CREDENTIAL_PROPERTY_PARSERS.size() + 1 == std::variant_size_v<CredentialConfig>);
58
59
// These visitors are the runtime extension points. Do not add a catch-all
60
// template overload: explicit overloads make a new CredentialConfig type fail
61
// to compile until its hashing and validation are implemented.
62
struct CredentialHashVisitor {
63
    uint64_t* hash;
64
65
2.29k
    void operator()(const std::monostate&) const {}
66
67
0
    void operator()(const GcpCredentialConfig& credential) const {
68
0
        *hash ^= static_cast<size_t>(credential.provider_type) + 1;
69
0
        *hash ^= std::hash<std::string> {}(credential.impersonation_service_account);
70
0
    }
71
};
72
73
struct CredentialValidationVisitor {
74
    const ObjCredentialValidationContext& context;
75
76
3.93k
    std::optional<std::string> operator()(const std::monostate&) const { return std::nullopt; }
77
78
21
    std::optional<std::string> operator()(const GcpCredentialConfig& credential) const {
79
21
        if (context.provider != ObjCredentialProvider::Gcp) {
80
2
            return "GCP credentials require provider=GCP";
81
2
        }
82
19
        if (context.has_conflicting_credentials()) {
83
6
            return "GCP credentials cannot be combined with other credentials";
84
6
        }
85
13
        return validate_gcp_credential(credential);
86
19
    }
87
};
88
89
struct CredentialTypeNameVisitor {
90
64
    std::string_view operator()(const std::monostate&) const { return "none"; }
91
4
    std::string_view operator()(const GcpCredentialConfig&) const { return "gcp"; }
92
};
93
94
} // namespace
95
96
2.29k
void hash_combine_credential(uint64_t* hash, const CredentialConfig& credential) {
97
2.29k
    *hash ^= credential.index();
98
2.29k
    std::visit(CredentialHashVisitor {hash}, credential);
99
2.29k
}
100
101
68
std::string_view credential_type_name(const CredentialConfig& credential) {
102
68
    return std::visit(CredentialTypeNameVisitor {}, credential);
103
68
}
104
105
std::optional<std::string> validate_obj_credential_config(
106
3.95k
        const CredentialConfig& credential, const ObjCredentialValidationContext& context) {
107
3.95k
    return std::visit(CredentialValidationVisitor {context}, credential);
108
3.95k
}
109
110
std::optional<std::string> parse_obj_credential_properties(
111
1.67k
        const ObjCredentialPropertyGetter& get_property, CredentialConfig* credential) {
112
1.67k
    CredentialConfig selected;
113
114
1.67k
    for (auto parser : CREDENTIAL_PROPERTY_PARSERS) {
115
1.67k
        auto result = parser(get_property);
116
1.67k
        if (result.error.has_value()) {
117
5
            return std::move(result.error);
118
5
        }
119
1.66k
        if (std::holds_alternative<std::monostate>(result.credential)) {
120
1.65k
            continue;
121
1.65k
        }
122
17
        if (!std::holds_alternative<std::monostate>(selected)) {
123
0
            return "multiple object storage credential types are configured";
124
0
        }
125
17
        selected = std::move(result.credential);
126
17
    }
127
128
1.66k
    if (!std::holds_alternative<std::monostate>(selected)) {
129
16
        *credential = std::move(selected);
130
16
    }
131
1.66k
    return std::nullopt;
132
1.67k
}
133
134
} // namespace doris