Skip to content
This repository has been archived by the owner on Apr 17, 2024. It is now read-only.

Commit

Permalink
Feat(Definitions): Released definitions for keycloak 21
Browse files Browse the repository at this point in the history
  • Loading branch information
DAHAG-ArisNourbakhsh committed Feb 24, 2023
1 parent 3a008a3 commit 7b8cac8
Show file tree
Hide file tree
Showing 12 changed files with 25,596 additions and 18 deletions.
3 changes: 3 additions & 0 deletions Dahag.Keycloak.OpenApiGenerator.Cli/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ public class Options

[Value(1, Required = false)]
public string? Output { get; set; }

[Value(2, Required = false)]
public string? OutputFileName { get; set; }
}
6 changes: 4 additions & 2 deletions Dahag.Keycloak.OpenApiGenerator.Cli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
if (!Directory.Exists(outputRoot))
throw new Exception($"output root directory '{outputRoot}' could not be found");
var outputFileName = options.OutputFileName ?? "definition";
var definitionsYmlPath = Path.Combine(outputRoot, $"{outputFileName}.yml");
var definitionsJsonPath = Path.Combine(outputRoot, $"{outputFileName}.json");
using var loggerFactory = new SerilogLoggerFactory();
var repoParser = new KeycloakRepositoryParser(loggerFactory.CreateLogger<KeycloakRepositoryParser>(), options.KeycloakRoot);
var apiDefinition = repoParser.Parse();
Expand All @@ -37,13 +41,11 @@
Log.Information("Generating OpenApi definition");
var openApiDocument = new KeycloakOpenApiGenerator().Generate(postProcessed.ActionCollections, postProcessed.Representations);
var definitionsYmlPath = Path.Combine(Directory.GetCurrentDirectory(), "definition.yml");
Log.Information($"Writing: {definitionsYmlPath}");
using (var targetFile = File.Open(definitionsYmlPath, FileMode.Create))
{
openApiDocument.SerializeAsYaml(targetFile, OpenApiSpecVersion.OpenApi3_0);
}
var definitionsJsonPath = Path.Combine(Directory.GetCurrentDirectory(), "definition.json");
Log.Information($"Writing: {definitionsJsonPath}");
using (var targetFile = File.Open(definitionsJsonPath, FileMode.Create))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<EmbeddedResource Include="TestFiles\ChildResource.java" />
<EmbeddedResource Include="TestFiles\DifferentConsumeAndProduceTypesEndpointsResource.java" />
<EmbeddedResource Include="TestFiles\DocumentedResource.java" />
<EmbeddedResource Include="TestFiles\ImplicitParamsResource.java" />
<EmbeddedResource Include="TestFiles\ImplicitPathResource.java" />
<EmbeddedResource Include="TestFiles\ParentResource.java" />
<EmbeddedResource Include="TestFiles\RepresentationWithIgnored.java" />
Expand Down
21 changes: 21 additions & 0 deletions Dahag.Keycloak.OpenApiGenerator.Tests/ResourceInterpreterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,27 @@ public void Interpret_AdvancedParams_Works()
}
});
}


[Test]
public void Interpret_ImplicitBodyParams_Works()
{
var actual = ParseResource("ImplicitParamsResource.java");

Assert.That(actual.Actions, Has.Count.EqualTo(1));
actual.Actions[0].Parameters.AssertEquality(new List<RawRxjsParam>
{
new()
{
Name = "data",
ParamSource = ParamSource.Body,
Type = "Object",
PathParam = null,
Default = null,
Implicit = true
}
});
}


[Test]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.keycloak.services.resources.admin;

import com.google.common.collect.Streams;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.broker.provider.IdentityProvider;
import org.keycloak.broker.provider.IdentityProviderFactory;
import org.keycloak.broker.social.SocialIdentityProvider;
import org.keycloak.connections.httpclient.HttpClientProvider;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.http.FormPartValue;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.models.utils.StripSecretsUtils;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.services.ErrorResponse;
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;

import javax.ws.rs.BadRequestException;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;

import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
import org.keycloak.utils.ReservedCharValidator;

/**
* @resource Identity Providers
* @author Pedro Igor
*/
public class IdentityProvidersResource {

private final RealmModel realm;
private final KeycloakSession session;
private AdminPermissionEvaluator auth;
private AdminEventBuilder adminEvent;

public IdentityProvidersResource(RealmModel realm, KeycloakSession session, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
this.realm = realm;
this.session = session;
this.auth = auth;
this.adminEvent = adminEvent.resource(ResourceType.IDENTITY_PROVIDER);
}

/**
* Import identity provider from uploaded JSON file
*
* @param input
* @return
* @throws IOException
*/
@POST
@Path("import-config")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
public Map<String, String> importFrom() throws IOException {
this.auth.realm().requireManageIdentityProviders();
MultivaluedMap<String, FormPartValue> formDataMap = session.getContext().getHttpRequest().getMultiPartFormParameters();
if (!(formDataMap.containsKey("providerId") && formDataMap.containsKey("file"))) {
throw new BadRequestException();
}
String providerId = formDataMap.getFirst("providerId").asString();
InputStream inputStream = formDataMap.getFirst("file").asInputStream();
IdentityProviderFactory providerFactory = getProviderFactoryById(providerId);
Map<String, String> config = providerFactory.parseConfig(session, inputStream);
return config;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ private OpenApiPaths CreatePaths(IEnumerable<IActionCollection> actionCollection

foreach (var pathGroup in groupedByPath)
{

var pathItem = CreateOpenApoCreateOpenApiPathItem(pathGroup.ToList());
paths.Add(pathGroup.Key, pathItem);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Diagnostics;
using System.Text.RegularExpressions;
using Dahag.Keycloak.OpenApiGenerator.Parsing.Documentation;

namespace Dahag.Keycloak.OpenApiGenerator.Parsing.Resource;
Expand Down Expand Up @@ -26,9 +27,9 @@ public override int GetHashCode()
public HttpMethod? HttpMethod { get; set; }
public List<RawRxjsParam> Parameters { get; set; } = new();
public RawDocumentation? Documentation { get; set; }
public List<MediaType>? Consumes { get; set; }
public List<MediaType>? Consumes { get; set; }
public List<MediaType>? Produces { get; set; }

public string? ReturnsType
{
get => _returnsType;
Expand All @@ -46,22 +47,23 @@ public string? ReturnsType
public int FoundAtLine { get; set; }
public int PersistedAtLine { get; set; }
public bool ProbablyParentOfAnotherResource { get; set; }
public string? RawMethodBody { get; set; }
public string? Tag { get; set; }

private string? _returnsType;


public void Set(ActionAnnotation actionAnnotation)
{
if (actionAnnotation.Path != null && Path != null)
throw new AlreadySetException(nameof(Path));

if (actionAnnotation.HttpMethod != null && HttpMethod != null)
throw new AlreadySetException(nameof(HttpMethod));

if (actionAnnotation.Consumes != null && Consumes != null)
throw new AlreadySetException(nameof(Consumes));

if (actionAnnotation.Produces != null && Produces != null)
throw new AlreadySetException(nameof(Produces));

Expand All @@ -70,14 +72,33 @@ public void Set(ActionAnnotation actionAnnotation)

if (actionAnnotation.HttpMethod != null)
HttpMethod = actionAnnotation.HttpMethod;

if (actionAnnotation.Consumes != null)
Consumes = actionAnnotation.Consumes;

if (actionAnnotation.Produces != null)
Produces = actionAnnotation.Produces;
}

public bool CouldMaybeHaveAnImplicitBodyParameter()
{
return Path != null
&& HttpMethod is Resource.HttpMethod.Post or Resource.HttpMethod.Put
&& ProbablyParentOfAnotherResource == false
&& (Consumes?.Any() ?? false)
&& (!Parameters.Any() || Parameters.All(x => x.ParamSource == ParamSource.Path))
&& RawMethodBody != null && (RawMethodBody.Contains(".getMultiPartFormParameters()") || RawMethodBody.Contains(".getDecodedFormParameters()") ||
new Regex(@"get.+FromRequest\(\)").IsMatch(RawMethodBody));
}

public void AddImplicitBodyParameter()
{
Parameters = Parameters.Concat(new List<RawRxjsParam>
{
RawRxjsParam.CreateImplicitBodyParameter()
}).ToList();
}

public override string ToString()
{
var paramsAsString = string.Join('\n', Parameters.Select(x => x.ToString()));
Expand Down
24 changes: 21 additions & 3 deletions Dahag.Keycloak.OpenApiGenerator/Parsing/Resource/RawRxjsParam.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,38 @@
namespace Dahag.Keycloak.OpenApiGenerator.Parsing.Resource;
using System.Diagnostics;

namespace Dahag.Keycloak.OpenApiGenerator.Parsing.Resource;

[DebuggerDisplay($"{nameof(ToString)}()")]
public class RawRxjsParam
{
public string? Name { get; set; }
public string? PathParam { get; set; }
public string? Type { get; set; }
public string? Default { get; set; }
public bool InternalJavaJankToIgnore { get; set; }
public bool Implicit { get; set; }
public ParamSource? ParamSource { get; set; }

public override string ToString()
{
var ignore = InternalJavaJankToIgnore ? $"IGNORE": "";
var ignore = InternalJavaJankToIgnore ? "IGNORE": "";
var defaultExpression = Default != null? $" = {Default}": "";
var paramSourceExpression = ParamSource != null? Enum.GetName(ParamSource.Value): "";
return $"{ignore}({paramSourceExpression}){PathParam}({Name}:{Type}){defaultExpression}";
var implicitExpression = Implicit ? "implicit": "explicit";
return $"{ignore}({implicitExpression})({paramSourceExpression}){PathParam}({Name}:{Type}){defaultExpression}";
}

public static RawRxjsParam CreateImplicitBodyParameter()
{
return new RawRxjsParam
{
Name = "data",
ParamSource = Resource.ParamSource.Body,
Type = "Object",
PathParam = null,
Default = null,
Implicit = true
};
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Diagnostics;
using Antlr4.Runtime;
using Antlr4.Runtime.Tree;

namespace Dahag.Keycloak.OpenApiGenerator.Parsing.Resource;
Expand Down Expand Up @@ -96,16 +97,28 @@ public override RawRxJsResource VisitMethodBody(JavaParser.MethodBodyContext con
_skipChildren = true;
if (CurrentPending != null)
{
CurrentPending.PersistedAtLine = context.Start.Line;
_resource.Actions.Add(CurrentPending);
CurrentPending = null;
FinalizeCurrentPending(context);
}

var result = base.VisitMethodBody(context);
_skipChildren = false;
return result;
}

private void FinalizeCurrentPending(ParserRuleContext context)
{
CurrentPending!.PersistedAtLine = context.Start.Line;
CurrentPending.RawMethodBody = context.GetText();
_resource.Actions.Add(CurrentPending);

if (CurrentPending.CouldMaybeHaveAnImplicitBodyParameter())
{
CurrentPending.AddImplicitBodyParameter();
}

CurrentPending = null;
}

public override RawRxJsResource VisitCompilationUnit(JavaParser.CompilationUnitContext context)
{
_resource.Name = context.typeDeclaration()[0].classDeclaration().identifier().GetText();
Expand Down
8 changes: 5 additions & 3 deletions Dahag.Keycloak.OpenApiGenerator/PostProcess/Parameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ public class Parameter : IParameter
public string Name { get; }
public string? Default { get; set; }
public TypeInfo TypeInfo { get; }
public bool Implicit { get; }

public Parameter(ParamSource source, string name, TypeInfo typeInfo)
public Parameter(ParamSource source, string name, TypeInfo typeInfo, bool @implicit)
{
Source = source;
Name = name;
TypeInfo = typeInfo;
Implicit = @implicit;
}

public static IParameter Create(RawRxjsParam raw, RawDocumentation? rawDocumentation, List<IRepresentation> representations, List<KeycloakEnum> enums)
Expand All @@ -45,10 +47,10 @@ public static IParameter Create(RawRxjsParam raw, RawDocumentation? rawDocumenta
if (typeInfo == null)
throw new Exception($"Could not find type for {raw.Type}");

return new Parameter(raw.ParamSource!.Value, raw.PathParam ?? raw.Name, typeInfo)
return new Parameter(raw.ParamSource!.Value, raw.PathParam ?? raw.Name, typeInfo, raw.Implicit)
{
Default = raw.Default,
Documentation = rawDocumentation?.ParamText.GetValueOrDefault(raw.Name)
Documentation = rawDocumentation?.ParamText.GetValueOrDefault(raw.Name),
};
}
}
Expand Down
Loading

0 comments on commit 7b8cac8

Please sign in to comment.