001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2008-2009 Sun Microsystems, Inc. 015 * Portions Copyright 2014-2016 ForgeRock AS. 016 */ 017package org.opends.server.tasks; 018 019import static org.opends.messages.TaskMessages.*; 020import static org.opends.server.util.ServerConstants.*; 021import static org.opends.server.util.StaticUtils.*; 022 023import org.forgerock.i18n.LocalizableMessage; 024import org.forgerock.i18n.slf4j.LocalizedLogger; 025import org.forgerock.opendj.ldap.ByteString; 026import org.forgerock.opendj.ldap.ResultCode; 027import org.opends.server.api.ClientConnection; 028import org.opends.server.api.ConnectionHandler; 029import org.opends.server.backends.task.Task; 030import org.opends.server.backends.task.TaskState; 031import org.opends.server.core.DirectoryServer; 032import org.opends.server.types.Attribute; 033import org.forgerock.opendj.ldap.schema.AttributeType; 034import org.opends.server.types.DirectoryException; 035import org.opends.server.types.DisconnectReason; 036import org.opends.server.types.Entry; 037import org.opends.server.types.Operation; 038import org.opends.server.types.Privilege; 039 040/** 041 * This class provides an implementation of a Directory Server task that can be 042 * used to terminate a client connection. 043 */ 044public class DisconnectClientTask extends Task 045{ 046 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 047 048 /** Indicates whether to send a notification message to the client. */ 049 private boolean notifyClient; 050 051 /** The connection ID for the client connection to terminate. */ 052 private long connectionID; 053 054 /** The disconnect message to send to the client. */ 055 private LocalizableMessage disconnectMessage; 056 057 /** {@inheritDoc} */ 058 @Override 059 public LocalizableMessage getDisplayName() { 060 return INFO_TASK_DISCONNECT_CLIENT_NAME.get(); 061 } 062 063 /** {@inheritDoc} */ 064 @Override 065 public void initializeTask() throws DirectoryException 066 { 067 // If the client connection is available, then make sure the client has the 068 // DISCONNECT_CLIENT privilege. 069 Operation operation = getOperation(); 070 if (operation != null) 071 { 072 ClientConnection conn = operation.getClientConnection(); 073 if (! conn.hasPrivilege(Privilege.DISCONNECT_CLIENT, operation)) 074 { 075 LocalizableMessage message = ERR_TASK_DISCONNECT_NO_PRIVILEGE.get(); 076 throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, 077 message); 078 } 079 } 080 081 final Entry taskEntry = getTaskEntry(); 082 connectionID = getConnectionID(taskEntry); 083 if (connectionID < 0) 084 { 085 LocalizableMessage message = 086 ERR_TASK_DISCONNECT_NO_CONN_ID.get(ATTR_TASK_DISCONNECT_CONN_ID); 087 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message); 088 } 089 090 notifyClient = mustNotifyClient(taskEntry); 091 disconnectMessage = getDisconnectMessage(taskEntry); 092 } 093 094 private long getConnectionID(Entry taskEntry) throws DirectoryException 095 { 096 final AttributeType attrType = DirectoryServer.getSchema().getAttributeType(ATTR_TASK_DISCONNECT_CONN_ID); 097 for (Attribute a : taskEntry.getAttribute(attrType)) 098 { 099 for (ByteString v : a) 100 { 101 try 102 { 103 return Long.parseLong(v.toString()); 104 } 105 catch (Exception e) 106 { 107 LocalizableMessage message = ERR_TASK_DISCONNECT_INVALID_CONN_ID.get(v); 108 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message, e); 109 } 110 } 111 } 112 return -1; 113 } 114 115 private boolean mustNotifyClient(Entry taskEntry) throws DirectoryException 116 { 117 final AttributeType attrType = DirectoryServer.getSchema().getAttributeType(ATTR_TASK_DISCONNECT_NOTIFY_CLIENT); 118 for (Attribute a : taskEntry.getAttribute(attrType)) 119 { 120 for (ByteString v : a) 121 { 122 final String stringValue = toLowerCase(v.toString()); 123 if ("true".equals(stringValue)) 124 { 125 return true; 126 } 127 else if ("false".equals(stringValue)) 128 { 129 return false; 130 } 131 else 132 { 133 LocalizableMessage message = ERR_TASK_DISCONNECT_INVALID_NOTIFY_CLIENT.get(stringValue); 134 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message); 135 } 136 } 137 } 138 return false; 139 } 140 141 private LocalizableMessage getDisconnectMessage(Entry taskEntry) 142 { 143 AttributeType attrType = DirectoryServer.getSchema().getAttributeType(ATTR_TASK_DISCONNECT_MESSAGE); 144 for (Attribute a : taskEntry.getAttribute(attrType)) 145 { 146 for (ByteString v : a) 147 { 148 return LocalizableMessage.raw(v.toString()); 149 } 150 } 151 return INFO_TASK_DISCONNECT_GENERIC_MESSAGE.get(); 152 } 153 154 @Override 155 protected TaskState runTask() 156 { 157 final ClientConnection clientConnection = getClientConnection(); 158 if (clientConnection == null) 159 { 160 logger.error(ERR_TASK_DISCONNECT_NO_SUCH_CONNECTION, connectionID); 161 return TaskState.COMPLETED_WITH_ERRORS; 162 } 163 164 clientConnection.disconnect(DisconnectReason.ADMIN_DISCONNECT, notifyClient, disconnectMessage); 165 return TaskState.COMPLETED_SUCCESSFULLY; 166 } 167 168 private ClientConnection getClientConnection() 169 { 170 for (ConnectionHandler<?> handler : DirectoryServer.getConnectionHandlers()) 171 { 172 for (ClientConnection c : handler.getClientConnections()) 173 { 174 if (c.getConnectionID() == connectionID) 175 { 176 return c; 177 } 178 } 179 } 180 return null; 181 } 182}