Skip to content

Commit e7dfbf4

Browse files
Merge pull request #199 from atc-net/hotfix/authorization-fix
authorization fix
2 parents 04b9367 + 76b682a commit e7dfbf4

File tree

41 files changed

+566
-129
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+566
-129
lines changed

Diff for: Directory.Build.props

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
<ItemGroup Label="Code Analyzers">
4444
<PackageReference Include="AsyncFixer" Version="1.6.0" PrivateAssets="All" />
4545
<PackageReference Include="Asyncify" Version="0.9.7" PrivateAssets="All" />
46-
<PackageReference Include="Meziantou.Analyzer" Version="2.0.161" PrivateAssets="All" />
46+
<PackageReference Include="Meziantou.Analyzer" Version="2.0.162" PrivateAssets="All" />
4747
<PackageReference Include="SecurityCodeScan.VS2019" Version="5.6.7" PrivateAssets="All" />
4848
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="All" />
4949
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.30.0.95878" PrivateAssets="All" />

Diff for: README.md

+2
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,8 @@ If all path-items (operations) under a given path all have x-authentication-requ
386386

387387
Authentication-Schemes and Authorize-Roles defined at path/controller level is taken into consideration when generating [Authorize] attributes for path-item/action/method level.
388388

389+
If no path-items (operations) under a given path have the x-authentication-required extension set, then no attributes will be generated for that given path/controller. If you want to force e.g [Authorize] or [AllowAnonymous], set the x-authentication-required extension to `true` or `false` respectively.
390+
389391
### Example
390392

391393
> NOTE: Tags, parameters, responses, request-bodies, schemas etc. are removed for brevity, so the references in spec below are not valid - The specification is only illustrating the various places the 3 new extension tags can be applied.

Diff for: src/Atc.CodeGeneration.CSharp/Atc.CodeGeneration.CSharp.csproj

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
</PropertyGroup>
77

88
<ItemGroup>
9-
<PackageReference Include="Atc" Version="2.0.498" />
10-
<PackageReference Include="Atc.CodeDocumentation" Version="2.0.498" />
9+
<PackageReference Include="Atc" Version="2.0.500" />
10+
<PackageReference Include="Atc.CodeDocumentation" Version="2.0.500" />
1111
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.10.0" />
1212
</ItemGroup>
1313

Diff for: src/Atc.Rest.ApiGenerator.CLI/Atc.Rest.ApiGenerator.CLI.csproj

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
</PropertyGroup>
1414

1515
<ItemGroup>
16-
<PackageReference Include="Atc" Version="2.0.498" />
17-
<PackageReference Include="Atc.Console.Spectre" Version="2.0.498" />
16+
<PackageReference Include="Atc" Version="2.0.500" />
17+
<PackageReference Include="Atc.Console.Spectre" Version="2.0.500" />
1818
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
1919
</ItemGroup>
2020

Diff for: src/Atc.Rest.ApiGenerator.Client.CSharp/ContentGenerators/ContentGeneratorClientEndpoint.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public string Generate()
104104
sb.AppendLine(8, "var responseBuilder = httpMessageFactory.FromResponse(response);");
105105

106106
var responseModels = parameters.ResponseModels
107-
.AppendUnauthorizedIfNeeded(parameters.Authorization)
107+
.AppendUnauthorizedIfNeeded(parameters.Authorization, parameters.IsAuthorizationRequiredFromPath)
108108
.AppendForbiddenIfNeeded(parameters.Authorization)
109109
.AppendBadRequestIfNeeded(parameters.ParameterName)
110110
.OrderBy(x => x.StatusCode)

Diff for: src/Atc.Rest.ApiGenerator.Client.CSharp/ContentGenerators/ContentGeneratorClientEndpointResult.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ private void AppendContentIsStatus(
6767
StringBuilder sb)
6868
{
6969
var responseModels = parameters.ResponseModels
70-
.AppendUnauthorizedIfNeeded(parameters.Authorization)
70+
.AppendUnauthorizedIfNeeded(parameters.Authorization, parameters.IsAuthorizationRequiredFromPath)
7171
.AppendForbiddenIfNeeded(parameters.Authorization)
7272
.AppendBadRequestIfNeeded(parameters.HasParameterType)
7373
.OrderBy(x => x.StatusCode)
@@ -202,7 +202,7 @@ private void AppendContentWithProblemDetails(
202202
StringBuilder sb)
203203
{
204204
var responseModels = parameters.ResponseModels
205-
.AppendUnauthorizedIfNeeded(parameters.Authorization)
205+
.AppendUnauthorizedIfNeeded(parameters.Authorization, parameters.IsAuthorizationRequiredFromPath)
206206
.AppendForbiddenIfNeeded(parameters.Authorization)
207207
.AppendBadRequestIfNeeded(parameters.HasParameterType)
208208
.OrderBy(x => x.StatusCode)
@@ -301,7 +301,7 @@ private void AppendContentWithoutProblemDetails(
301301
StringBuilder sb)
302302
{
303303
var responseModels = parameters.ResponseModels
304-
.AppendUnauthorizedIfNeeded(parameters.Authorization)
304+
.AppendUnauthorizedIfNeeded(parameters.Authorization, parameters.IsAuthorizationRequiredFromPath)
305305
.AppendForbiddenIfNeeded(parameters.Authorization)
306306
.AppendBadRequestIfNeeded(parameters.HasParameterType)
307307
.OrderBy(x => x.StatusCode)

Diff for: src/Atc.Rest.ApiGenerator.Client.CSharp/ContentGenerators/ContentGeneratorClientEndpointResultInterface.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ private void AppendContentIsStatus(
6464
StringBuilder sb)
6565
{
6666
var responseModels = parameters.ResponseModels
67-
.AppendUnauthorizedIfNeeded(parameters.Authorization)
67+
.AppendUnauthorizedIfNeeded(parameters.Authorization, parameters.IsAuthorizationRequiredFromPath)
6868
.AppendForbiddenIfNeeded(parameters.Authorization)
6969
.AppendBadRequestIfNeeded(parameters.HasParameterType)
7070
.OrderBy(x => x.StatusCode)
@@ -183,7 +183,7 @@ private void AppendContentWithProblemDetails(
183183
StringBuilder sb)
184184
{
185185
var responseModels = parameters.ResponseModels
186-
.AppendUnauthorizedIfNeeded(parameters.Authorization)
186+
.AppendUnauthorizedIfNeeded(parameters.Authorization, parameters.IsAuthorizationRequiredFromPath)
187187
.AppendForbiddenIfNeeded(parameters.Authorization)
188188
.AppendBadRequestIfNeeded(parameters.HasParameterType)
189189
.OrderBy(x => x.StatusCode)
@@ -276,7 +276,7 @@ private void AppendContentWithoutProblemDetails(
276276
StringBuilder sb)
277277
{
278278
var responseModels = parameters.ResponseModels
279-
.AppendUnauthorizedIfNeeded(parameters.Authorization)
279+
.AppendUnauthorizedIfNeeded(parameters.Authorization, parameters.IsAuthorizationRequiredFromPath)
280280
.AppendForbiddenIfNeeded(parameters.Authorization)
281281
.AppendBadRequestIfNeeded(parameters.HasParameterType)
282282
.OrderBy(x => x.StatusCode)

Diff for: src/Atc.Rest.ApiGenerator.CodingRules/Atc.Rest.ApiGenerator.CodingRules.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
</PropertyGroup>
77

88
<ItemGroup>
9-
<PackageReference Include="Atc" Version="2.0.498" />
9+
<PackageReference Include="Atc" Version="2.0.500" />
1010
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
1111
</ItemGroup>
1212

Diff for: src/Atc.Rest.ApiGenerator.Contracts/Atc.Rest.ApiGenerator.Contracts.csproj

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
</PropertyGroup>
77

88
<ItemGroup>
9-
<PackageReference Include="Atc" Version="2.0.498" />
10-
<PackageReference Include="Atc.CodeDocumentation" Version="2.0.498" />
9+
<PackageReference Include="Atc" Version="2.0.500" />
10+
<PackageReference Include="Atc.CodeDocumentation" Version="2.0.500" />
1111
</ItemGroup>
1212

1313
</Project>

Diff for: src/Atc.Rest.ApiGenerator.Contracts/ContentGeneratorsParameters/Client/ContentGeneratorClientEndpointParameters.cs

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public record ContentGeneratorClientEndpointParameters(
1212
string ResultName,
1313
string? ParameterName,
1414
ApiAuthorizeModel? Authorization,
15+
bool IsAuthorizationRequiredFromPath,
1516
IList<ApiOperationResponseModel> ResponseModels,
1617
IList<ContentGeneratorClientEndpointParametersParameters>? Parameters);
1718

Diff for: src/Atc.Rest.ApiGenerator.Contracts/ContentGeneratorsParameters/Client/ContentGeneratorClientEndpointResultInterfaceParameters.cs

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ public record ContentGeneratorClientEndpointResultInterfaceParameters(
88
string InheritInterfaceName,
99
bool HasParameterType,
1010
ApiAuthorizeModel? Authorization,
11+
bool IsAuthorizationRequiredFromPath,
1112
IList<ApiOperationResponseModel> ResponseModels);
1213

1314
public record ContentGeneratorClientEndpointResultInterfaceParametersParameters(

Diff for: src/Atc.Rest.ApiGenerator.Contracts/ContentGeneratorsParameters/Client/ContentGeneratorClientEndpointResultParameters.cs

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public record ContentGeneratorClientEndpointResultParameters(
99
string InheritClassName,
1010
bool HasParameterType,
1111
ApiAuthorizeModel? Authorization,
12+
bool IsAuthorizationRequiredFromPath,
1213
IList<ApiOperationResponseModel> ResponseModels,
1314
IList<ContentGeneratorClientEndpointResultParametersParameters>? Parameters);
1415

Diff for: src/Atc.Rest.ApiGenerator.Contracts/Extensions/ApiOperationResponseModelExtensions.cs

+13-3
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,27 @@ public static class ApiOperationResponseModelExtensions
3636

3737
public static IEnumerable<ApiOperationResponseModel> AppendUnauthorizedIfNeeded(
3838
this IEnumerable<ApiOperationResponseModel> responseModels,
39-
ApiAuthorizeModel? authorization)
39+
ApiAuthorizeModel? authorization,
40+
bool isRequiredFromPath)
4041
{
41-
if (authorization is null)
42+
if (authorization is null &&
43+
!isRequiredFromPath)
44+
{
45+
return responseModels;
46+
}
47+
48+
if (authorization is not null &&
49+
authorization.UseAllowAnonymous)
4250
{
4351
return responseModels;
4452
}
4553

4654
var models = responseModels.ToList();
4755

56+
var useAllowAnonymous = authorization?.UseAllowAnonymous ?? false;
57+
4858
if (models.TrueForAll(x => x.StatusCode != HttpStatusCode.Unauthorized) &&
49-
!authorization.UseAllowAnonymous)
59+
(isRequiredFromPath || !useAllowAnonymous))
5060
{
5161
models.Add(
5262
new ApiOperationResponseModel(

Diff for: src/Atc.Rest.ApiGenerator.Framework.Minimal/Atc.Rest.ApiGenerator.Framework.Minimal.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
</PropertyGroup>
77

88
<ItemGroup>
9-
<PackageReference Include="Atc" Version="2.0.498" />
9+
<PackageReference Include="Atc" Version="2.0.500" />
1010
</ItemGroup>
1111

1212
<ItemGroup>

Diff for: src/Atc.Rest.ApiGenerator.Framework.Minimal/ContentGenerators/ContentGeneratorServerEndpoints.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ private static void AppendProducesWithProblemDetails(
186186
ContentGeneratorServerEndpointMethodParameters item)
187187
{
188188
var responseModels = item.ResponseModels
189-
.AppendUnauthorizedIfNeeded(item.Authorization)
189+
.AppendUnauthorizedIfNeeded(item.Authorization, item.IsAuthorizationRequiredFromPath)
190190
.AppendForbiddenIfNeeded(item.Authorization)
191191
.AppendBadRequestIfNeeded(item.ParameterTypeName)
192192
.OrderBy(x => x.StatusCode)
@@ -288,7 +288,7 @@ private static void AppendProducesWithoutProblemDetails(
288288
ContentGeneratorServerEndpointMethodParameters item)
289289
{
290290
var responseModels = item.ResponseModels
291-
.AppendUnauthorizedIfNeeded(item.Authorization)
291+
.AppendUnauthorizedIfNeeded(item.Authorization, item.IsAuthorizationRequiredFromPath)
292292
.AppendForbiddenIfNeeded(item.Authorization)
293293
.AppendBadRequestIfNeeded(item.ParameterTypeName)
294294
.OrderBy(x => x.StatusCode)

Diff for: src/Atc.Rest.ApiGenerator.Framework.Mvc/Atc.Rest.ApiGenerator.Framework.Mvc.csproj

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
</PropertyGroup>
77

88
<ItemGroup>
9-
<PackageReference Include="Atc" Version="2.0.498" />
10-
<PackageReference Include="Atc.Rest" Version="2.0.498" />
9+
<PackageReference Include="Atc" Version="2.0.500" />
10+
<PackageReference Include="Atc.Rest" Version="2.0.500" />
1111
</ItemGroup>
1212

1313
<ItemGroup>

Diff for: src/Atc.Rest.ApiGenerator.Framework.Mvc/ContentGenerators/ContentGeneratorServerController.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ private static void AppendProducesWithProblemDetails(
174174
ContentGeneratorServerEndpointMethodParameters item)
175175
{
176176
var responseModels = item.ResponseModels
177-
.AppendUnauthorizedIfNeeded(item.Authorization)
177+
.AppendUnauthorizedIfNeeded(item.Authorization, item.IsAuthorizationRequiredFromPath)
178178
.AppendForbiddenIfNeeded(item.Authorization)
179179
.AppendBadRequestIfNeeded(item.ParameterTypeName)
180180
.OrderBy(x => x.StatusCode)
@@ -265,7 +265,7 @@ private static void AppendProducesWithoutProblemDetails(
265265
ContentGeneratorServerEndpointMethodParameters item)
266266
{
267267
var responseModels = item.ResponseModels
268-
.AppendUnauthorizedIfNeeded(item.Authorization)
268+
.AppendUnauthorizedIfNeeded(item.Authorization, item.IsAuthorizationRequiredFromPath)
269269
.AppendForbiddenIfNeeded(item.Authorization)
270270
.AppendBadRequestIfNeeded(item.ParameterTypeName)
271271
.OrderBy(x => x.StatusCode)

Diff for: src/Atc.Rest.ApiGenerator.Framework/Atc.Rest.ApiGenerator.Framework.csproj

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@
2626
</ItemGroup>
2727

2828
<ItemGroup>
29-
<PackageReference Include="Atc" Version="2.0.498" />
30-
<PackageReference Include="Atc.DotNet" Version="2.0.498" />
31-
<PackageReference Include="Atc.OpenApi" Version="2.0.498" />
29+
<PackageReference Include="Atc" Version="2.0.500" />
30+
<PackageReference Include="Atc.DotNet" Version="2.0.500" />
31+
<PackageReference Include="Atc.OpenApi" Version="2.0.500" />
3232
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
3333
</ItemGroup>
3434

Diff for: src/Atc.Rest.ApiGenerator.Framework/ContentGeneratorsParameters/Server/ContentGeneratorServerEndpointMethodParameters.cs

+1
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ public record ContentGeneratorServerEndpointMethodParameters(
1111
long? MultipartBodyLengthLimit,
1212
string ResultName,
1313
ApiAuthorizeModel? Authorization,
14+
bool IsAuthorizationRequiredFromPath,
1415
IEnumerable<ApiOperationResponseModel> ResponseModels);

Diff for: src/Atc.Rest.ApiGenerator.Framework/Factories/Parameters/Client/ContentGeneratorClientEndpointParametersFactory.cs

+3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public static ContentGeneratorClientEndpointParameters Create(
2323

2424
var modelNamespace = $"{projectName}.{ContentGeneratorConstants.Contracts}.{apiGroupName}";
2525
var operationName = openApiOperation.GetOperationName();
26+
var controllerAuthorization = openApiPath.ExtractApiPathAuthorization();
2627
var endpointAuthorization = openApiOperation.ExtractApiOperationAuthorization(openApiPath);
2728
var responseModels = openApiOperation.ExtractApiOperationResponseModels(modelNamespace).ToList();
2829

@@ -41,6 +42,7 @@ public static ContentGeneratorClientEndpointParameters Create(
4142
ResultName: $"{operationName}{ContentGeneratorConstants.EndpointResult}",
4243
ParameterName: $"{operationName}{ContentGeneratorConstants.Parameters}",
4344
Authorization: endpointAuthorization,
45+
IsAuthorizationRequiredFromPath: controllerAuthorization is not null,
4446
ResponseModels: responseModels,
4547
parameters);
4648
}
@@ -57,6 +59,7 @@ public static ContentGeneratorClientEndpointParameters Create(
5759
ResultName: $"{operationName}{ContentGeneratorConstants.EndpointResult}",
5860
ParameterName: null,
5961
Authorization: endpointAuthorization,
62+
IsAuthorizationRequiredFromPath: controllerAuthorization is not null,
6063
ResponseModels: responseModels,
6164
Parameters: null);
6265
}

Diff for: src/Atc.Rest.ApiGenerator.Framework/Factories/Parameters/Client/ContentGeneratorClientEndpointResultInterfaceParametersFactory.cs

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public static ContentGeneratorClientEndpointResultInterfaceParameters Create(
2020

2121
var modelNamespace = $"{projectName}.{ContentGeneratorConstants.Contracts}.{apiGroupName}";
2222
var operationName = openApiOperation.GetOperationName();
23+
var controllerAuthorization = openApiPath.ExtractApiPathAuthorization();
2324
var endpointAuthorization = openApiOperation.ExtractApiOperationAuthorization(openApiPath);
2425
var responseModels = openApiOperation.ExtractApiOperationResponseModels(modelNamespace).ToList();
2526
var hasParameterType = openApiPath.HasParameters() || openApiOperation.HasParametersOrRequestBody();
@@ -32,6 +33,7 @@ public static ContentGeneratorClientEndpointResultInterfaceParameters Create(
3233
InheritInterfaceName: "IEndpointResponse",
3334
HasParameterType: hasParameterType,
3435
Authorization: endpointAuthorization,
36+
IsAuthorizationRequiredFromPath: controllerAuthorization is not null,
3537
ResponseModels: responseModels);
3638
}
3739

Diff for: src/Atc.Rest.ApiGenerator.Framework/Factories/Parameters/Client/ContentGeneratorClientEndpointResultParametersFactory.cs

+3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public static ContentGeneratorClientEndpointResultParameters Create(
2020

2121
var modelNamespace = $"{projectName}.{ContentGeneratorConstants.Contracts}.{apiGroupName}";
2222
var operationName = openApiOperation.GetOperationName();
23+
var controllerAuthorization = openApiPath.ExtractApiPathAuthorization();
2324
var endpointAuthorization = openApiOperation.ExtractApiOperationAuthorization(openApiPath);
2425
var responseModels = openApiOperation.ExtractApiOperationResponseModels(modelNamespace).ToList();
2526
var hasParameterType = openApiPath.HasParameters() || openApiOperation.HasParametersOrRequestBody();
@@ -35,6 +36,7 @@ public static ContentGeneratorClientEndpointResultParameters Create(
3536
InheritClassName: ContentGeneratorConstants.EndpointResponse,
3637
HasParameterType: hasParameterType,
3738
Authorization: endpointAuthorization,
39+
IsAuthorizationRequiredFromPath: controllerAuthorization is not null,
3840
ResponseModels: responseModels,
3941
parameters);
4042
}
@@ -48,6 +50,7 @@ public static ContentGeneratorClientEndpointResultParameters Create(
4850
InheritClassName: ContentGeneratorConstants.EndpointResponse,
4951
HasParameterType: hasParameterType,
5052
Authorization: endpointAuthorization,
53+
IsAuthorizationRequiredFromPath: controllerAuthorization is not null,
5154
ResponseModels: responseModels,
5255
Parameters: null);
5356
}

Diff for: src/Atc.Rest.ApiGenerator.Framework/Factories/Server/ContentGeneratorServerEndpointParametersFactory.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ public static ContentGeneratorServerEndpointParameters Create(
3838
MultipartBodyLengthLimit: GetMultipartBodyLengthLimit(apiOperation.Value),
3939
ResultName: $"{operationName}{ContentGeneratorConstants.Result}",
4040
ResponseModels: responseModels,
41-
Authorization: endpointAuthorization));
41+
Authorization: endpointAuthorization,
42+
IsAuthorizationRequiredFromPath: controllerAuthorization is not null));
4243
}
4344
}
4445

Diff for: src/Atc.Rest.ApiGenerator.Nuget/Atc.Rest.ApiGenerator.Nuget.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
</PropertyGroup>
77

88
<ItemGroup>
9-
<PackageReference Include="Atc" Version="2.0.498" />
9+
<PackageReference Include="Atc" Version="2.0.500" />
1010
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
1111
</ItemGroup>
1212

Diff for: src/Atc.Rest.ApiGenerator.OpenApi/Atc.Rest.ApiGenerator.OpenApi.csproj

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
</PropertyGroup>
77

88
<ItemGroup>
9-
<PackageReference Include="Atc" Version="2.0.498" />
10-
<PackageReference Include="Atc.CodeDocumentation" Version="2.0.498" />
11-
<PackageReference Include="Atc.OpenApi" Version="2.0.498" />
9+
<PackageReference Include="Atc" Version="2.0.500" />
10+
<PackageReference Include="Atc.CodeDocumentation" Version="2.0.500" />
11+
<PackageReference Include="Atc.OpenApi" Version="2.0.500" />
1212
<PackageReference Include="Microsoft.OpenApi" Version="1.6.15" />
1313
<PackageReference Include="Microsoft.OpenApi.Readers" Version="1.6.15" />
1414
</ItemGroup>

0 commit comments

Comments
 (0)