001: package org.jgroups.protocols;
002:
003: import org.jgroups.Address;
004: import org.jgroups.Event;
005: import org.jgroups.Message;
006: import org.jgroups.auth.AuthToken;
007: import org.jgroups.protocols.pbcast.GMS;
008: import org.jgroups.protocols.pbcast.JoinRsp;
009: import org.jgroups.stack.Protocol;
010: import java.util.Properties;
011:
012: /**
013: * The AUTH protocol adds a layer of authentication to JGroups
014: * @author Chris Mills
015: */
016: public class AUTH extends Protocol {
017:
018: static final String NAME = "AUTH";
019:
020: /**
021: * used on the coordinator to authentication joining member requests against
022: */
023: private AuthToken serverSideToken = null;
024:
025: public AUTH() {
026: }
027:
028: public boolean setProperties(Properties props) {
029:
030: String authClassString = props.getProperty("auth_class");
031:
032: if (authClassString != null) {
033: props.remove("auth_class");
034:
035: try {
036: Object obj = Class.forName(authClassString)
037: .newInstance();
038: serverSideToken = (AuthToken) obj;
039: serverSideToken.setValue(props);
040: } catch (Exception e) {
041: if (log.isFatalEnabled()) {
042: log.fatal("Failed to create server side token ("
043: + authClassString + ")");
044: log.fatal(e);
045: }
046: return false;
047: }
048: }
049:
050: if (props.size() > 0) {
051: //this should never happen as everything is read in to the AuthToken instance
052: if (log.isErrorEnabled()) {
053: log
054: .error("AUTH.setProperties(): the following properties are not recognized: "
055: + props);
056: }
057: return false;
058: }
059: return true;
060: }
061:
062: public final String getName() {
063: return AUTH.NAME;
064: }
065:
066: /**
067: * Used to create a failed JOIN_RSP message to pass back down the stack
068: * @param joiner The Address of the requesting member
069: * @param message The failure message to send back to the joiner
070: * @return An Event containing a GmsHeader with a JoinRsp object
071: */
072: private Event createFailureEvent(Address joiner, String message) {
073: Message msg = new Message(joiner, null, null);
074:
075: if (log.isDebugEnabled()) {
076: log.debug("Creating JoinRsp with failure message - "
077: + message);
078: }
079: JoinRsp joinRes = new JoinRsp(message);
080: //need to specify the error message on the JoinRsp object once it's been changed
081:
082: GMS.GmsHeader gmsHeader = new GMS.GmsHeader(
083: GMS.GmsHeader.JOIN_RSP, joinRes);
084: msg.putHeader(GMS.name, gmsHeader);
085:
086: if (log.isDebugEnabled()) {
087: log.debug("GMSHeader created for failure JOIN_RSP");
088: }
089:
090: return new Event(Event.MSG, msg);
091: }
092:
093: /**
094: * An event was received from the layer below. Usually the current layer will want to examine
095: * the event type and - depending on its type - perform some computation
096: * (e.g. removing headers from a MSG event type, or updating the internal membership list
097: * when receiving a VIEW_CHANGE event).
098: * Finally the event is either a) discarded, or b) an event is sent down
099: * the stack using <code>passDown()</code> or c) the event (or another event) is sent up
100: * the stack using <code>passUp()</code>.
101: */
102: public void up(Event evt) {
103: GMS.GmsHeader hdr = isJoinMessage(evt);
104: if ((hdr != null) && (hdr.getType() == GMS.GmsHeader.JOIN_REQ)) {
105: if (log.isDebugEnabled()) {
106: log.debug("AUTH got up event");
107: }
108: //we found a join message - now try and get the AUTH Header
109: Message msg = (Message) evt.getArg();
110:
111: if ((msg.getHeader(AUTH.NAME) != null)
112: && (msg.getHeader(AUTH.NAME) instanceof AuthHeader)) {
113: AuthHeader authHeader = (AuthHeader) msg
114: .getHeader(AUTH.NAME);
115:
116: if (authHeader != null) {
117: //Now we have the AUTH Header we need to validate it
118: if (this .serverSideToken.authenticate(authHeader
119: .getToken(), msg)) {
120: //valid token
121: if (log.isDebugEnabled()) {
122: log.debug("AUTH passing up event");
123: }
124: passUp(evt);
125: } else {
126: //invalid token
127: if (log.isWarnEnabled()) {
128: log
129: .warn("AUTH failed to validate AuthHeader token");
130: }
131: passDown(createFailureEvent(msg.getSrc(),
132: "Authentication failed"));
133: }
134: } else {
135: //Invalid AUTH Header - need to send failure message
136: if (log.isWarnEnabled()) {
137: log
138: .warn("AUTH failed to get valid AuthHeader from Message");
139: }
140: passDown(createFailureEvent(msg.getSrc(),
141: "Failed to find valid AuthHeader in Message"));
142: }
143: } else {
144: if (log.isDebugEnabled()) {
145: log.debug("No AUTH Header Found");
146: }
147: //should be a failure
148: passDown(createFailureEvent(msg.getSrc(),
149: "Failed to find an AuthHeader in Message"));
150: }
151: } else {
152: //if debug
153: if (log.isDebugEnabled()) {
154: log.debug("Message not a JOIN_REQ - ignoring it");
155: }
156: passUp(evt);
157: }
158: }
159:
160: /**
161: * An event is to be sent down the stack. The layer may want to examine its type and perform
162: * some action on it, depending on the event's type. If the event is a message MSG, then
163: * the layer may need to add a header to it (or do nothing at all) before sending it down
164: * the stack using <code>passDown()</code>. In case of a GET_ADDRESS event (which tries to
165: * retrieve the stack's address from one of the bottom layers), the layer may need to send
166: * a new response event back up the stack using <code>passUp()</code>.
167: */
168: public void down(Event evt) {
169: GMS.GmsHeader hdr = isJoinMessage(evt);
170: if ((hdr != null) && (hdr.getType() == GMS.GmsHeader.JOIN_REQ)) {
171: if (log.isDebugEnabled()) {
172: log.debug("AUTH got down event");
173: }
174: //we found a join request message - now add an AUTH Header
175: Message msg = (Message) evt.getArg();
176: AuthHeader authHeader = new AuthHeader();
177: authHeader.setToken(this .serverSideToken);
178: msg.putHeader(AUTH.NAME, authHeader);
179:
180: if (log.isDebugEnabled()) {
181: log.debug("AUTH passing down event");
182: }
183: }
184:
185: if ((hdr != null) && (hdr.getType() == GMS.GmsHeader.JOIN_RSP)) {
186: if (log.isDebugEnabled()) {
187: log.debug(hdr.toString());
188: }
189: }
190:
191: passDown(evt);
192: }
193:
194: /**
195: * Used to check if the message type is a Gms message
196: * @param evt The event object passed in to AUTH
197: * @return A GmsHeader object or null if the event contains a message of a different type
198: */
199: private static GMS.GmsHeader isJoinMessage(Event evt) {
200: Message msg;
201: switch (evt.getType()) {
202: case Event.MSG:
203: msg = (Message) evt.getArg();
204: Object obj = msg.getHeader("GMS");
205: if (obj == null || !(obj instanceof GMS.GmsHeader)) {
206: return null;
207: }
208: return (GMS.GmsHeader) obj;
209: }
210: return null;
211: }
212: }
|