1 /*
2  * JBoss, Home of Professional Open Source.
3  * Copyright 2015 Red Hat, Inc., and individual contributors
4  * as indicated by the @author tags.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18 package io.undertow.security.impl;
19
20 import static io.undertow.UndertowMessages.MESSAGES;
21
22 import io.undertow.UndertowLogger;
23 import io.undertow.security.api.NotificationReceiver;
24 import io.undertow.security.api.SecurityContext;
25 import io.undertow.security.api.SecurityNotification;
26 import io.undertow.security.api.SecurityNotification.EventType;
27 import io.undertow.security.idm.Account;
28 import io.undertow.server.HttpServerExchange;
29
30 /**
31  * A base class for {@link SecurityContext} implementations predominantly focusing on the notification handling allowing the
32  * specific implementation for focus on authentication.
33  *
34  * @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a>
35  */

36 public abstract class AbstractSecurityContext implements SecurityContext {
37
38     private boolean authenticationRequired;
39     protected final HttpServerExchange exchange;
40
41     private Node<NotificationReceiver> notificationReceivers = null;
42
43     private Account account;
44     private String mechanismName;
45
46     protected AbstractSecurityContext(final HttpServerExchange exchange) {
47         this.exchange = exchange;
48     }
49
50     @Override
51     public void setAuthenticationRequired() {
52         authenticationRequired = true;
53     }
54
55     @Override
56     public boolean isAuthenticationRequired() {
57         return authenticationRequired;
58     }
59
60     @Override
61     public boolean isAuthenticated() {
62         return account != null;
63     }
64
65     @Override
66     public Account getAuthenticatedAccount() {
67         return account;
68     }
69
70     /**
71      * @return The name of the mechanism used to authenticate the request.
72      */

73     @Override
74     public String getMechanismName() {
75         return mechanismName;
76     }
77
78     @Override
79     public void authenticationComplete(Account account, String mechanism, final boolean cachingRequired) {
80         authenticationComplete(account, mechanism, false, cachingRequired);
81     }
82
83     protected void authenticationComplete(Account account, String mechanism, boolean programatic, final boolean cachingRequired) {
84         this.account = account;
85         this.mechanismName = mechanism;
86
87         UndertowLogger.SECURITY_LOGGER.debugf("Authenticated as %s, roles %s", account.getPrincipal().getName(), account.getRoles());
88         sendNoticiation(new SecurityNotification(exchange, EventType.AUTHENTICATED, account, mechanism, programatic,
89                 MESSAGES.userAuthenticated(account.getPrincipal().getName()), cachingRequired));
90     }
91
92     @Override
93     public void authenticationFailed(String message, String mechanism) {
94         UndertowLogger.SECURITY_LOGGER.debugf("Authentication failed with message %s and mechanism %s for %s", message, mechanism, exchange);
95         sendNoticiation(new SecurityNotification(exchange, EventType.FAILED_AUTHENTICATION, null, mechanism, false, message, true));
96     }
97
98     @Override
99     public void registerNotificationReceiver(NotificationReceiver receiver) {
100         if(notificationReceivers == null) {
101             notificationReceivers = new Node<>(receiver);
102         } else {
103             Node<NotificationReceiver> cur = notificationReceivers;
104             while (cur.next != null) {
105                 cur = cur.next;
106             }
107             cur.next = new Node<>(receiver);
108         }
109     }
110
111     @Override
112     public void removeNotificationReceiver(NotificationReceiver receiver) {
113         Node<NotificationReceiver> cur = notificationReceivers;
114         if(receiver.equals(cur.item)) {
115             notificationReceivers = cur.next;
116         } else {
117             Node<NotificationReceiver> old = cur;
118             while (cur.next != null) {
119                 cur = cur.next;
120                 if(receiver.equals(cur.item)) {
121                     old.next = cur.next;
122                 }
123                 old = cur;
124             }
125         }
126     }
127
128     private void sendNoticiation(final SecurityNotification notification) {
129         Node<NotificationReceiver> cur = notificationReceivers;
130         while (cur != null) {
131             cur.item.handleNotification(notification);
132             cur = cur.next;
133         }
134     }
135
136     @Override
137     public void logout() {
138         if (!isAuthenticated()) {
139             return;
140         }
141         UndertowLogger.SECURITY_LOGGER.debugf("Logged out %s", exchange);
142         sendNoticiation(new SecurityNotification(exchange, SecurityNotification.EventType.LOGGED_OUT, account, mechanismName, true,
143                 MESSAGES.userLoggedOut(account.getPrincipal().getName()), true));
144
145         this.account = null;
146         this.mechanismName = null;
147     }
148
149     /**
150      * To reduce allocations we use a custom linked list data structure
151      * @param <T>
152      */

153     protected static final class Node<T> {
154         final T item;
155         Node<T> next;
156
157         private Node(T item) {
158             this.item = item;
159         }
160     }
161
162 }
163