///|
priv enum AuthScheme {
Basic
Bearer
Digest
ApiKey
} derive(Eq, Hash)
///|
fn AuthScheme::id(self : AuthScheme) -> @smithy.ShapeId {
@smithy.ShapeId::parse(
match self {
Basic => "smithy.api#httpBasicAuth"
Bearer => "smithy.api#httpBearerAuth"
Digest => "smithy.api#httpDigestAuth"
ApiKey => "smithy.api#httpApiKeyAuth"
},
).unwrap()
}
///|
fn AuthScheme::component(self : AuthScheme) -> String {
let id = self.id()
id.namespace() + "." + id.name()
}
///|
fn Context::effective_auth(
self : Context,
subject : @smithy.Shape,
) -> Array[AuthScheme] {
let available = [Basic, Bearer, Digest, ApiKey].filter(scheme => {
self.service.traits.contains(scheme.id())
})
let auth = match subject.trait_value("smithy.api#auth") {
Some(@smithy.Array(values)) => Some(values)
_ if subject.id != self.service.id =>
match self.service.trait_value("smithy.api#auth") {
Some(@smithy.Array(values)) => Some(values)
_ => None
}
_ => None
}
match auth {
None => available
Some(values) => {
let selected : Set[@smithy.ShapeId] = Set([])
for value in values {
if value is @smithy.String(name) &&
@smithy.ShapeId::parse(name) is Some(id) {
selected.add(id)
}
}
available.filter(scheme => selected.contains(scheme.id()))
}
}
}
///|
fn Context::security_requirements(
self : Context,
schemes : Array[AuthScheme],
) -> Json {
Json::array(
schemes.map(scheme => {
self.auth_used.add(scheme)
let requirement : Map[String, Json] = Map([])
requirement[scheme.component()] = Json::array([])
Json::object(requirement)
}),
)
}
///|
fn Context::operation_security(
self : Context,
operation : @smithy.Shape,
fields : Map[String, Json],
) -> Unit {
let effective = self.effective_auth(operation)
if operation.trait_value("smithy.api#auth") == Some(@smithy.Array([])) ||
effective != self.effective_auth(self.service) {
fields["security"] = self.security_requirements(effective)
}
}
///|
fn Context::security_schemes(
self : Context,
) -> Map[String, Json] raise OpenApiError {
let schemas : Map[String, Json] = Map([])
for scheme in [Basic, Bearer, Digest, ApiKey] {
if !self.auth_used.contains(scheme) {
continue
}
let fields : Map[String, Json] = { "type": Json::string("http") }
match scheme {
Basic => {
fields["scheme"] = Json::string("Basic")
fields["description"] = Json::string("HTTP Basic authentication")
}
Bearer => {
fields["scheme"] = Json::string("Bearer")
fields["description"] = Json::string("HTTP Bearer authentication")
}
Digest => {
fields["scheme"] = Json::string("Digest")
fields["description"] = Json::string("HTTP Digest authentication")
}
ApiKey => {
guard self.service.traits.get(scheme.id())
is Some(@smithy.Object(trait_fields)) &&
trait_fields.get("name") is Some(@smithy.String(name)) &&
trait_fields.get("in") is Some(@smithy.String(location)) else {
raise InvalidConfiguration(
"HTTP API key authentication requires name and in",
)
}
fields["type"] = Json::string("apiKey")
fields["name"] = Json::string(name)
fields["in"] = Json::string(location)
fields["description"] = Json::string(
"API key authentication via the '" +
name +
"' " +
(if location == "query" {
" query string parameter"
} else {
location
}),
)
if trait_fields.get("scheme") is Some(@smithy.String(scheme_name)) {
fields["type"] = Json::string("http")
fields["scheme"] = Json::string(scheme_name)
}
}
}
schemas[scheme.component()] = Json::object(fields)
}
schemas
}