Skip to content

Commit

Permalink
Merge pull request #14 from ligangty/main
Browse files Browse the repository at this point in the history
Support old yaml file for new multiple roles way
  • Loading branch information
ligangty authored Sep 5, 2023
2 parents 2263e04 + e69f156 commit 120460a
Show file tree
Hide file tree
Showing 8 changed files with 311 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,24 @@
*/
package org.commonjava.indy.service.security.common;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

import java.util.Arrays;
import java.util.List;

@JsonDeserialize( using = SecurityConstraintDeSerializer.class )
@SuppressWarnings( "unused" )
public class SecurityConstraint
{

@JsonProperty( "urlPattern" )
private String urlPattern;

@JsonProperty( "roles" )
private List<String> roles;

@JsonProperty( "methods" )
private List<String> methods;

public SecurityConstraint( String urlPattern, List<String> roles, List<String> methods )
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/**
* Copyright (C) 2023 Red Hat, Inc.
*
* 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.commonjava.indy.service.security.common;

import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.dataformat.yaml.JacksonYAMLParseException;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import static com.fasterxml.jackson.databind.node.JsonNodeType.STRING;

public class SecurityConstraintDeSerializer
extends StdDeserializer<SecurityConstraint>
{
public SecurityConstraintDeSerializer()
{
this( null );
}

public SecurityConstraintDeSerializer( Class<?> vc )
{
super( vc );
}

@Override
public SecurityConstraint deserialize( JsonParser p, DeserializationContext ctxt )
throws IOException, JacksonException
{
JsonNode node = p.getCodec().readTree( p );
JsonNode rolesNode = node.get( "roles" );
if ( rolesNode == null )
{
rolesNode = node.get( "role" );
if ( rolesNode == null )
{
throw new JacksonYAMLParseException( p, "\"roles\" should not be empty!", null );
}
}
List<String> roles = handleVariableNode( p, rolesNode );

JsonNode urlPatNode = node.get( "urlPattern" );
if ( urlPatNode == null )
{
throw new JacksonYAMLParseException( p, "\"urlPattern\" should not be empty!", null );
}
final String urlPattern;
if ( urlPatNode.getNodeType() == STRING )
{
urlPattern = urlPatNode.textValue();
}
else
{
throw new JacksonYAMLParseException( p, "\"urlPattern\" should be string type!", null );
}

JsonNode methodsNode = node.get( "methods" );
if ( methodsNode == null )
{
throw new JacksonYAMLParseException( p, "\"methods\" should not be empty!", null );
}
List<String> methods = handleVariableNode( p, methodsNode );

return new SecurityConstraint( urlPattern, roles, methods );
}

private List<String> handleVariableNode( JsonParser p, JsonNode node )
throws JacksonException
{
List<String> result = new ArrayList<>();
switch ( node.getNodeType() )
{
case ARRAY:
for ( JsonNode child : node )
{
if ( child.getNodeType() == STRING )
{
final String value = child.textValue();
result.add( value );
}
}
break;
case STRING:
final String[] resultStr = node.textValue().split( "," );
for ( String rt : resultStr )
{
result.add( rt.trim() );
}
break;
default:
throw new JacksonYAMLParseException( p, "\"roles\" should be string or list type!", null );
}
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@ public void init()
final File constraintFile = new File( loc );
if ( !constraintFile.isFile() )
{
logger.warn( "Cannot load security constraints: {}, will try to load from classpath.",
constraintFile );
logger.warn( "Cannot load security constraints: {}, will try to load from classpath.", constraintFile );
}
else
{
Expand Down Expand Up @@ -119,8 +118,7 @@ public void init()
private void parseBindings( InputStream input )
throws IOException
{
final ObjectMapper mapper = new ObjectMapper( new YAMLFactory() );
mapper.findAndRegisterModules();
final ObjectMapper mapper = new ObjectMapper( new YAMLFactory() ).findAndRegisterModules();
constraintSet = mapper.readValue( input, SecurityBindings.class );
if ( constraintSet != null )
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/**
* Copyright (C) 2023 Red Hat, Inc.
*
* 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.commonjava.indy.service.security.common;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class SecurityConstraintTest
{
private final ObjectMapper mapper = new ObjectMapper( new YAMLFactory() ).findAndRegisterModules();

@Test
public void testDeSerilizationArrayType()
throws Exception
{
assertContraints( "security-bindings.yaml" );
}

@Test
public void testDeSerilizationStringType()
throws Exception
{
assertContraints( "security-bindings-string.yaml" );
}

@Test
public void testDeSerilizationHyrbidType()
throws Exception
{
assertContraints( "security-bindings-hybrid.yaml" );
}

@Test
public void testDeSerilizationHyrbidType2()
throws Exception
{
assertContraints( "security-bindings-hybrid2.yaml" );
}

@Test
public void testDeSerilizationOld()
throws Exception
{
assertContraints( "security-bindings-old.yaml" );
}

private void assertContraints( final String inputFile )
throws IOException
{
try (InputStream input = this.getClass().getClassLoader().getResourceAsStream( inputFile ))
{
SecurityBindings constraintSet = mapper.readValue( input, SecurityBindings.class );
if ( constraintSet != null )
{
List<SecurityConstraint> constraints = constraintSet.getConstraints();
assertThat( constraints.size(), is( 3 ) );
for ( SecurityConstraint constraint : constraints )
{
switch ( constraint.getUrlPattern() )
{
case "/api/admin/.*":
assertContraintValues( constraint.getMethods(), 3, "POST", "PUT", "DELETE" );
assertContraintValues( constraint.getRoles(), 1, "admin" );
break;
case "/api/.*":
assertContraintValues( constraint.getMethods(), 3, "POST", "PUT", "DELETE" );
assertContraintValues( constraint.getRoles(), 1, "user" );
break;
case "/api/admin/stores/.*":
assertContraintValues( constraint.getMethods(), 3, "POST", "PUT", "DELETE" );
assertContraintValues( constraint.getRoles(), 2, "admin", "power-user" );
break;
default:
Assertions.fail(
String.format( "%s pattern should not exist!", constraint.getUrlPattern() ) );
}
}
}
}
}

private void assertContraintValues( List<String> list, int expectSize, String... expectedContents )
{
assertThat( list.size(), is( expectSize ) );
for ( String content : expectedContents )
{
assertTrue( list.contains( content ) );
}
}

}
20 changes: 20 additions & 0 deletions src/test/resources/security-bindings-hybrid.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
constraints:
- urlPattern: "/api/admin/.*"
roles: admin
methods:
- POST
- PUT
- DELETE
- urlPattern: "/api/.*"
roles: user
methods:
- POST
- PUT
- DELETE
- urlPattern: "/api/admin/stores/.*"
roles: power-user,admin
methods:
- POST
- PUT
- DELETE

22 changes: 22 additions & 0 deletions src/test/resources/security-bindings-hybrid2.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
constraints:
- urlPattern: "/api/admin/.*"
roles: admin
methods:
- POST
- PUT
- DELETE
- urlPattern: "/api/.*"
roles: user
methods:
- POST
- PUT
- DELETE
- urlPattern: "/api/admin/stores/.*"
roles:
- power-user
- admin
methods:
- POST
- PUT
- DELETE

22 changes: 22 additions & 0 deletions src/test/resources/security-bindings-old.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
constraints:
- urlPattern: "/api/admin/.*"
role: admin
methods:
- POST
- PUT
- DELETE
- urlPattern: "/api/.*"
role: user
methods:
- POST
- PUT
- DELETE
- urlPattern: "/api/admin/stores/.*"
role:
- power-user
- admin
methods:
- POST
- PUT
- DELETE

11 changes: 11 additions & 0 deletions src/test/resources/security-bindings-string.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
constraints:
- urlPattern: "/api/admin/.*"
roles: admin
methods: POST,PUT,DELETE
- urlPattern: "/api/.*"
roles: user
methods: POST,PUT,DELETE
- urlPattern: "/api/admin/stores/.*"
roles: power-user,admin
methods: POST,PUT,DELETE

0 comments on commit 120460a

Please sign in to comment.