1
1
package run .halo .app .theme .dialect ;
2
2
3
3
import static org .springframework .security .core .authority .AuthorityUtils .createAuthorityList ;
4
+ import static org .thymeleaf .extras .springsecurity6 .dialect .processor .AuthorizeAttrProcessor .ATTR_NAME ;
5
+ import static org .thymeleaf .extras .springsecurity6 .dialect .processor .AuthorizeAttrProcessor .ATTR_PRECEDENCE ;
4
6
import static run .halo .app .infra .AnonymousUserConst .PRINCIPAL ;
5
7
import static run .halo .app .infra .AnonymousUserConst .Role ;
6
8
9
+ import java .util .LinkedHashSet ;
10
+ import java .util .Optional ;
11
+ import java .util .Set ;
7
12
import java .util .function .Function ;
8
13
import org .springframework .beans .factory .InitializingBean ;
14
+ import org .springframework .beans .factory .ObjectProvider ;
15
+ import org .springframework .security .access .expression .ExpressionUtils ;
16
+ import org .springframework .security .access .expression .method .MethodSecurityExpressionHandler ;
9
17
import org .springframework .security .authentication .AnonymousAuthenticationToken ;
18
+ import org .springframework .security .core .Authentication ;
10
19
import org .springframework .security .core .context .SecurityContextImpl ;
20
+ import org .springframework .security .util .MethodInvocationUtils ;
11
21
import org .springframework .security .web .server .context .ServerSecurityContextRepository ;
12
22
import org .springframework .web .server .ServerWebExchange ;
23
+ import org .thymeleaf .context .ITemplateContext ;
24
+ import org .thymeleaf .engine .AttributeName ;
25
+ import org .thymeleaf .extras .springsecurity6 .auth .AuthUtils ;
13
26
import org .thymeleaf .extras .springsecurity6 .dialect .SpringSecurityDialect ;
14
27
import org .thymeleaf .extras .springsecurity6 .util .SpringSecurityContextUtils ;
28
+ import org .thymeleaf .extras .springsecurity6 .util .SpringVersionSpecificUtils ;
15
29
import org .thymeleaf .extras .springsecurity6 .util .SpringVersionUtils ;
30
+ import org .thymeleaf .model .IProcessableElementTag ;
31
+ import org .thymeleaf .processor .IProcessor ;
32
+ import org .thymeleaf .standard .processor .AbstractStandardConditionalVisibilityTagProcessor ;
33
+ import org .thymeleaf .templatemode .TemplateMode ;
16
34
import run .halo .app .security .authorization .AuthorityUtils ;
17
35
18
36
/**
@@ -28,8 +46,12 @@ public class HaloSpringSecurityDialect extends SpringSecurityDialect implements
28
46
29
47
private final ServerSecurityContextRepository securityContextRepository ;
30
48
31
- public HaloSpringSecurityDialect (ServerSecurityContextRepository securityContextRepository ) {
49
+ private final ObjectProvider <MethodSecurityExpressionHandler > expressionHandler ;
50
+
51
+ public HaloSpringSecurityDialect (ServerSecurityContextRepository securityContextRepository ,
52
+ ObjectProvider <MethodSecurityExpressionHandler > expressionHandler ) {
32
53
this .securityContextRepository = securityContextRepository ;
54
+ this .expressionHandler = expressionHandler ;
33
55
}
34
56
35
57
@ Override
@@ -53,4 +75,81 @@ public void afterPropertiesSet() {
53
75
// Just overwrite the value of the attribute
54
76
getExecutionAttributes ().put (SECURITY_CONTEXT_EXECUTION_ATTRIBUTE_NAME , secCtxInitializer );
55
77
}
78
+
79
+ @ Override
80
+ public Set <IProcessor > getProcessors (String dialectPrefix ) {
81
+ LinkedHashSet <IProcessor > processors = new LinkedHashSet <>();
82
+ processors .add (
83
+ new HaloAuthorizeAttrProcessor (TemplateMode .HTML , dialectPrefix , ATTR_NAME )
84
+ );
85
+ processors .addAll (super .getProcessors (dialectPrefix ));
86
+ return processors ;
87
+ }
88
+
89
+ public class HaloAuthorizeAttrProcessor
90
+ extends AbstractStandardConditionalVisibilityTagProcessor {
91
+
92
+ protected HaloAuthorizeAttrProcessor (TemplateMode templateMode , String dialectPrefix ,
93
+ String attrName ) {
94
+ super (templateMode , dialectPrefix , attrName , ATTR_PRECEDENCE - 10 );
95
+ }
96
+
97
+ @ Override
98
+ protected boolean isVisible (ITemplateContext context , IProcessableElementTag tag ,
99
+ AttributeName attributeName , String attributeValue ) {
100
+
101
+ final String attrValue = (attributeValue == null ? null : attributeValue .trim ());
102
+
103
+ if (attrValue == null || attrValue .isEmpty ()) {
104
+ return false ;
105
+ }
106
+
107
+ final Authentication authentication = AuthUtils .getAuthenticationObject (context );
108
+
109
+ if (authentication == null ) {
110
+ return false ;
111
+ }
112
+
113
+ // resolve expr
114
+ var expr = Optional .of (attributeValue )
115
+ .filter (v -> v .startsWith ("${" ) && v .endsWith ("}" ))
116
+ .map (v -> v .substring (2 , v .length () - 1 ))
117
+ .orElse (attributeValue );
118
+
119
+ var expressionHandler = HaloSpringSecurityDialect .this .expressionHandler .getIfUnique ();
120
+ if (expressionHandler == null ) {
121
+ // no expression handler found
122
+ return false ;
123
+ }
124
+
125
+ var expression = expressionHandler .getExpressionParser ().parseExpression (expr );
126
+
127
+ var methodInvocation = MethodInvocationUtils .createFromClass (this ,
128
+ HaloAuthorizeAttrProcessor .class ,
129
+ "dummyAuthorize" ,
130
+ new Class [] {Authentication .class },
131
+ new Object [] {authentication }
132
+ );
133
+ var evaluationContext =
134
+ expressionHandler .createEvaluationContext (authentication , methodInvocation );
135
+
136
+ var expressionObjects = context .getExpressionObjects ();
137
+ var wrappedEvolutionContext = SpringVersionSpecificUtils .wrapEvaluationContext (
138
+ evaluationContext , expressionObjects
139
+ );
140
+
141
+ return ExpressionUtils .evaluateAsBoolean (expression , wrappedEvolutionContext );
142
+ }
143
+
144
+ /**
145
+ * This method is only used to create a method invocation for the expression parser.
146
+ *
147
+ * @param authentication authentication object
148
+ * @return result of authorization expression evaluation
149
+ */
150
+ public Boolean dummyAuthorize (Authentication authentication ) {
151
+ throw new UnsupportedOperationException ("Should not be called" );
152
+ }
153
+
154
+ }
56
155
}
0 commit comments