Skip to content

Commit

Permalink
Adapted fr.adrienbrault.idea.symfony2plugin.security.AnnotationExpres…
Browse files Browse the repository at this point in the history
…sionGotoCompletionRegistrar
  • Loading branch information
adamwojs committed Sep 20, 2021
1 parent 2c10c84 commit 44eba02
Showing 1 changed file with 28 additions and 73 deletions.
Original file line number Diff line number Diff line change
@@ -1,48 +1,45 @@
package fr.adrienbrault.idea.symfony2plugin.security;

import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.patterns.PatternCondition;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.patterns.StandardPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.util.ProcessingContext;
import com.jetbrains.php.lang.documentation.phpdoc.lexer.PhpDocTokenTypes;
import com.jetbrains.php.lang.documentation.phpdoc.parser.PhpDocElementTypes;
import com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag;
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
import de.espend.idea.php.annotation.util.AnnotationUtil;
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionProvider;
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionProviderLookupArguments;
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionRegistrar;
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionRegistrarParameter;
import fr.adrienbrault.idea.symfony2plugin.expressionLanguage.psi.*;
import fr.adrienbrault.idea.symfony2plugin.security.utils.VoterUtil;
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
import org.jetbrains.annotations.NotNull;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* @author Daniel Espendiller <[email protected]>
*/
public class AnnotationExpressionGotoCompletionRegistrar implements GotoCompletionRegistrar {

private static final String SECURITY_ANNOTATION = "Sensio\\Bundle\\FrameworkExtraBundle\\Configuration\\Security";

@Override
public void register(@NotNull GotoCompletionRegistrarParameter registrar) {
// "@Security("is_granted('POST_SHOW', post) and has_role('ROLE_ADMIN')")"
registrar.register(
PlatformPatterns.psiElement(PhpDocTokenTypes.DOC_STRING)
.withParent(PlatformPatterns.psiElement(StringLiteralExpression.class)
.withParent(PlatformPatterns.psiElement(PhpDocElementTypes.phpDocAttributeList)
.withParent(PlatformPatterns.psiElement(PhpDocTag.class)
.with(PhpDocInstancePatternCondition.INSTANCE)
PlatformPatterns.psiElement()
.withParent(PlatformPatterns
.psiElement(ExpressionLanguageStringLiteral.class)
.withParent(PlatformPatterns
.psiElement(ExpressionLanguageLiteralExpr.class)
.withParent(PlatformPatterns
.psiElement(ExpressionLanguageCallExpr.class)
.withFirstChild(PlatformPatterns
.psiElement(ExpressionLanguageRefExpr.class)
.withText(StandardPatterns.string().oneOf("has_role", "is_granted"))
)
)
)
)
),
),
MyGotoCompletionProvider::new
);
}
Expand All @@ -59,75 +56,33 @@ private static class MyGotoCompletionProvider extends GotoCompletionProvider {
@Override
public void getLookupElements(@NotNull GotoCompletionProviderLookupArguments arguments) {
final CompletionResultSet resultSet = arguments.getResultSet();
String blockNamePrefix = resultSet.getPrefixMatcher().getPrefix();

// find caret position:
// - "has_role('"
// - "has_role('YAML_ROLE_"
if(!blockNamePrefix.matches("^.*(has_role|is_granted)\\s*\\(\\s*'[\\w-]*$")) {
return;
}

// clear prefix caret string; for a clean completion independent from inside content
CompletionResultSet myResultSet = resultSet.withPrefixMatcher("");

VoterUtil.LookupElementPairConsumer consumer = new VoterUtil.LookupElementPairConsumer();
VoterUtil.visitAttribute(getProject(), consumer);
myResultSet.addAllElements(consumer.getLookupElements());
resultSet.addAllElements(consumer.getLookupElements());
}

@NotNull
@Override
public Collection<PsiElement> getPsiTargets(PsiElement element) {
if(getElement().getNode().getElementType() != PhpDocTokenTypes.DOC_STRING) {
return Collections.emptyList();
}

PsiElement parent = getElement().getParent();
if(!(parent instanceof StringLiteralExpression)) {
if(getElement().getNode().getElementType() != ExpressionLanguageTypes.STRING) {
return Collections.emptyList();
}

String contents = ((StringLiteralExpression) parent).getContents();
var role = element.getText();
if (!StringUtil.isEmpty(role)) {
var targets = new HashSet<PsiElement>();

Collection<String> roles = new HashSet<>();
for (String regex : new String[]{"is_granted\\s*\\(\\s*['|\"]([^'\"]+)['|\"]\\s*[\\)|,]", "has_role\\s*\\(\\s*['|\"]([^'\"]+)['|\"]\\s*\\)"}) {
Matcher matcher = Pattern.compile(regex).matcher(contents);
while(matcher.find()){
roles.add(matcher.group(1));
}
}
VoterUtil.visitAttribute(getProject(), pair -> {
if(pair.getFirst().equals(role.substring(1, role.length() - 1))) {
targets.add(pair.getSecond());
}
});

if(roles.size() == 0) {
return Collections.emptyList();
return targets;
}

Collection<PsiElement> targets = new HashSet<>();

VoterUtil.visitAttribute(getProject(), pair -> {
if(roles.contains(pair.getFirst())) {
targets.add(pair.getSecond());
}
});

return targets;
}
}

/**
* Check if given PhpDocTag is instance of given Annotation class
*/
private static class PhpDocInstancePatternCondition extends PatternCondition<PsiElement> {
private static PhpDocInstancePatternCondition INSTANCE = new PhpDocInstancePatternCondition();

PhpDocInstancePatternCondition() {
super("PhpDoc Annotation Instance");
}

@Override
public boolean accepts(@NotNull PsiElement psiElement, ProcessingContext processingContext) {
return psiElement instanceof PhpDocTag
&& PhpElementsUtil.isEqualClassName(AnnotationUtil.getAnnotationReference((PhpDocTag) psiElement), SECURITY_ANNOTATION);
return Collections.emptyList();
}
}
}

0 comments on commit 44eba02

Please sign in to comment.