summaryrefslogtreecommitdiffstats
path: root/src/main/java/org/openslx/imagemaster/server/ApiServer.java
blob: e265045ced93c18d06c1c50839c3a9934a4ed0ef (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
package org.openslx.imagemaster.server;

import java.nio.ByteBuffer;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;
import org.openslx.encryption.AsymKeyHolder;
import org.openslx.imagemaster.db.DbImage;
import org.openslx.imagemaster.db.DbPendingSatellite;
import org.openslx.imagemaster.db.DbSatellite;
import org.openslx.imagemaster.db.DbUser;
import org.openslx.imagemaster.serverconnection.ImageProcessor;
import org.openslx.imagemaster.serversession.ServerAuthenticator;
import org.openslx.imagemaster.serversession.ServerSession;
import org.openslx.imagemaster.serversession.ServerSessionManager;
import org.openslx.imagemaster.serversession.ServerUser;
import org.openslx.imagemaster.session.Authenticator;
import org.openslx.imagemaster.session.Session;
import org.openslx.imagemaster.session.SessionManager;
import org.openslx.imagemaster.session.User;
import org.openslx.imagemaster.thrift.iface.AuthenticationError;
import org.openslx.imagemaster.thrift.iface.AuthenticationException;
import org.openslx.imagemaster.thrift.iface.AuthorizationError;
import org.openslx.imagemaster.thrift.iface.AuthorizationException;
import org.openslx.imagemaster.thrift.iface.DownloadData;
import org.openslx.imagemaster.thrift.iface.ImageData;
import org.openslx.imagemaster.thrift.iface.ImageDataError;
import org.openslx.imagemaster.thrift.iface.ImageDataException;
import org.openslx.imagemaster.thrift.iface.InvalidTokenException;
import org.openslx.imagemaster.thrift.iface.OrganizationData;
import org.openslx.imagemaster.thrift.iface.ServerSessionData;
import org.openslx.imagemaster.thrift.iface.SessionData;
import org.openslx.imagemaster.thrift.iface.UploadData;
import org.openslx.imagemaster.thrift.iface.UploadException;
import org.openslx.imagemaster.thrift.iface.UserInfo;
import org.openslx.imagemaster.util.Util;

/**
 * API Server This is where all the requests from the outside arrive. We don't
 * handle them directly in the Thrift handlers, as we might be adding other APIs
 * later, like JSON/SOAP/REST/HTTP/XML or some other stuff. They'd all just
 * interface with this static class here. Note that we use the exceptions from
 * the thrift interface that you can simply catch in any other API handler and
 * eg. transform into error codes, if the API doesn't support exceptions.
 * 
 * This will be accessed from multiple threads, so use synchronization when
 * needed (or in doubt)
 */
public class ApiServer
{

	private static final Logger LOG = Logger.getLogger( ApiServer.class );

	/**
	 * Request for authentication
	 * 
	 * @param login The user's login in the form "user@organization.com"
	 * @param password user's password
	 * @return SessionData struct with session id/token iff login successful
	 * @throws AuthenticationException if login not successful
	 */
	public static SessionData authenticate( String login, String password )
			throws AuthenticationException
	{
		if ( login == null || password == null ) {
			throw new AuthenticationException(
					AuthenticationError.INVALID_CREDENTIALS,
					"Empty username or password!" );
		}
		final User user = Authenticator.authenticate( login, password );

		final Session session = new Session( user );
		return SessionManager.addSession( session );
	}

	/**
	 * Request information about user for given token
	 * 
	 * @param token a user's token
	 * @return UserInfo struct for given token's user
	 * @throws InvalidTokenException if no user matches the given token
	 */
	public static UserInfo getUserFromToken( String token )
			throws InvalidTokenException
	{
		final Session session = SessionManager.getSessionFromToken( token );
		if ( session == null )
			throw new InvalidTokenException();
		return new UserInfo( session.getLogin(), session.getFirstName(),
				session.getLastName(), session.getEMail(), session.getOrgenizationId() );
	}

	public static UploadData submitImage( String serverSessionId, ImageData imageDescription, List<Integer> crcSums )
			throws AuthorizationException, ImageDataException, UploadException
	{
		// first check session of server
		if ( ServerSessionManager.getSession( serverSessionId ) == null ) {
			throw new AuthorizationException( AuthorizationError.NOT_AUTHENTICATED, "No valid serverSessionId" );
		}
		// then let the image processor decide what to do
		return ImageProcessor.getUploadInfos( imageDescription, crcSums );
	}

	public static DownloadData getImage( String uuid, String serverSessionId ) throws AuthorizationException, ImageDataException
	{
		// first check session of server
		if ( ServerSessionManager.getSession( serverSessionId ) == null ) {
			throw new AuthorizationException( AuthorizationError.NOT_AUTHENTICATED, "No valid serverSessionId" );
		}

		if ( !DbImage.exists( uuid ) ) {
			throw new ImageDataException( ImageDataError.UNKNOWN_IMAGE, "UUID is not known by this server." );
		}

		// then let the image processor decide what to do
		return ImageProcessor.getDownloadInfos( uuid );
	}

	/**
	 * Start the server authentication of a uni/hs satellite server.
	 * 
	 * @param organization the organization that the server belongs to
	 * @return a random string that needs to be encrypted with the private
	 *         key of the requesting satellite server
	 * @throws ServerAuthenticationException when organization is invalid/unknown
	 */
	public static ByteBuffer startServerAuthentication( String organizationId )
			throws AuthenticationException
	{
		if ( organizationId == null || organizationId.isEmpty() )
			throw new AuthenticationException( AuthenticationError.INVALID_ORGANIZATION, "Empty organization" );

		DbSatellite satellite = DbSatellite.fromOrganizationId( organizationId );
		if ( satellite == null )
			throw new AuthenticationException( AuthenticationError.INVALID_ORGANIZATION, "Unknown organization: '" + organizationId + "'" );
		if ( satellite.getPubkey() == null )
			throw new AuthenticationException( AuthenticationError.INVALID_KEY, "There is no public key known for your organization." );
		return ServerAuthenticator.startServerAuthentication( organizationId );
	}

	/**
	 * Authenticate the uni/hs satellite server with the encrypted string.
	 * 
	 * @param organization the organization that the server belongs to
	 * @param challengeResponse the encrypted string
	 * @return session data iff the authentication was successful
	 * @throws AuthenticationException
	 */
	public static ServerSessionData serverAuthenticate( String organizationId,
			ByteBuffer challengeResponse ) throws AuthenticationException
	{
		if ( organizationId == null || challengeResponse == null ) {
			throw new AuthenticationException( AuthenticationError.INVALID_ORGANIZATION, "Empty organization or challengeResponse" );
		}
		DbSatellite satellite = DbSatellite.fromOrganizationId( organizationId );
		if ( satellite == null )
			throw new AuthenticationException( AuthenticationError.INVALID_ORGANIZATION, "Unknown organization" );
		if ( satellite.getPubkey() == null )
			throw new AuthenticationException( AuthenticationError.INVALID_KEY, "There is no public key known for your organization." );

		final ServerUser serverUser = ServerAuthenticator.serverAuthenticate( satellite, challengeResponse );

		final ServerSession session = new ServerSession( serverUser );
		return ServerSessionManager.addSession( session );
	}

	public static boolean isServerAuthenticated( String serverSessionId )
	{
		return ( ServerSessionManager.getSession( serverSessionId ) != null );
	}

	public static boolean publishUser( String serverSessionId, UserInfo user ) throws AuthorizationException
	{
		// Check session.
		if ( SessionManager.getSessionFromSessionId( serverSessionId ) == null ) {
			throw new AuthorizationException( AuthorizationError.NOT_AUTHENTICATED, "Session ID not valid" );
		}
		if ( DbUser.forLogin( user.userId ) == null ) {
			// User not known by server. Insert into server database.
			return DbUser.insertOrUpdate( user );
		}
		// Else user is already known by server. Do nothing.
		return true;
	}

	public static List<UserInfo> findUser( String sessionId, String organizationId, String searchTerm ) throws AuthorizationException
	{
		// Needs to be a logged in user
		if ( SessionManager.getSessionFromSessionId( sessionId ) == null )
			throw new AuthorizationException( AuthorizationError.NOT_AUTHENTICATED, "Session ID not valid" );
		// Search string needs to be at least 2 characters (FIXME: quick and dirty ignoring LIKE chars) 
		if ( searchTerm == null || searchTerm.length() < 2 || searchTerm.replaceAll( "[%_]", "" ).length() < 2 )
			return new ArrayList<>( 0 );
		return DbUser.findUser( organizationId, searchTerm );
	}

	public static List<OrganizationData> getOrganizations()
	{
		return DbSatellite.asOrganizationDataList();
	}

	public static List<ImageData> getPublicImages( String sessionId, int page ) throws AuthorizationException
	{
		if ( SessionManager.getSessionFromSessionId( sessionId ) == null )
			throw new AuthorizationException( AuthorizationError.NOT_AUTHENTICATED, "Session ID not valid" );
		return DbImage.asImageDataList( page * 100, ( page + 1 ) * 100 );
	}

	public static boolean registerSatellite( String organizationId, String address, String modulus, String exponent )
	{
		if ( organizationId == null || address == null || exponent == null || modulus == null )
			return false;
		Key newKey;
		try {
			newKey = new AsymKeyHolder( null, Util.tryToParseBigInt( exponent ), Util.tryToParseBigInt( modulus ) ).getPublicKey();
		} catch ( NoSuchAlgorithmException | InvalidKeySpecException e ) {
			LOG.warn( "Invalid public key in registerOrganization for " + organizationId + " (" + address + ")", e );
			return false;
		}
		if ( newKey == null ) {
			LOG.warn( "Uninstantiable public key in registerOrganization for " + organizationId + " (" + address + ")" );
			return false;
		}
		DbSatellite existing = DbSatellite.fromSuffix( organizationId );
		if ( existing != null ) {
			Key existingKey = existing.getPubkey();
			if ( existingKey != null && Util.keysEqual( newKey, existingKey ) )
				return true;
		}
		return DbPendingSatellite.add( organizationId, address, modulus, exponent );
	}

	public static boolean updateSatelliteAddress( String serverSessionId, String address ) throws AuthorizationException
	{
		if ( ServerSessionManager.getSession( serverSessionId ) == null )
			throw new AuthorizationException( AuthorizationError.NOT_AUTHENTICATED, "No valid serverSessionId" );
		// TODO: Implement
		return false;
	}
}