blob: 9e732113b85ce2eb20498211b94c8c5f4cb5bfe8 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.kerby.has.server.web.rest;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.codec.binary.Base64;
import org.apache.kerby.has.common.HasConfig;
import org.apache.kerby.has.common.HasException;
import org.apache.kerby.has.common.util.HasUtil;
import org.apache.kerby.has.server.HasAuthenException;
import org.apache.kerby.has.server.HasServer;
import org.apache.kerby.has.server.HasServerPlugin;
import org.apache.kerby.has.server.HasServerPluginRegistry;
import org.apache.kerby.has.server.kdc.HasKdcHandler;
import org.apache.kerby.has.server.web.HostRoleType;
import org.apache.kerby.has.server.web.WebServer;
import org.apache.kerby.has.server.web.rest.param.AuthTokenParam;
import org.apache.kerby.has.server.web.rest.param.TypeParam;
import org.apache.hadoop.http.JettyUtils;
import org.apache.kerby.kerberos.kerb.KrbException;
import org.apache.kerby.kerberos.kerb.KrbRuntime;
import org.apache.kerby.kerberos.kerb.identity.backend.BackendConfig;
import org.apache.kerby.kerberos.kerb.provider.TokenDecoder;
import org.apache.kerby.kerberos.kerb.server.KdcUtil;
import org.apache.kerby.kerberos.kerb.type.base.AuthToken;
import org.apache.kerby.kerberos.kerb.type.base.KrbMessage;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.TreeMap;
/**
* HAS web methods implementation.
*/
@Path("")
public class HasApi {
@Context
private ServletContext context;
@Context
private HttpServletRequest httpRequest;
/**
* Get krb5.conf file.
*
* @return Response
*/
@GET
@Path("/getkrb5conf")
@Produces(MediaType.TEXT_PLAIN)
public Response getKrb5Conf() {
if (httpRequest.isSecure()) {
final HasServer hasServer = WebServer.getHasServerFromContext(context);
try {
BackendConfig backendConfig = KdcUtil.getBackendConfig(hasServer.getConfDir());
String backendJar = backendConfig.getString("kdc_identity_backend");
File conf;
if (backendJar.equals("org.apache.kerby.has.server.kdc.MySQLIdentityBackend")) {
conf = hasServer.generateKrb5Conf();
} else {
File confDir = hasServer.getConfDir();
conf = new File(confDir, "krb5.conf");
}
return Response.ok(conf).header("Content-Disposition", "attachment; filename=krb5.conf").build();
} catch (KrbException | HasException e) {
throw new RuntimeException("Failed to get Krb5.conf. ", e);
}
}
return Response.status(403).entity("HTTPS required.\n").build();
}
/**
* Get has-client.conf file.
*
* @return Response
*/
@GET
@Path("/gethasconf")
@Produces(MediaType.TEXT_PLAIN)
public Response getHasConf() {
if (httpRequest.isSecure()) {
final HasServer hasServer = WebServer.getHasServerFromContext(context);
try {
BackendConfig backendConfig = KdcUtil.getBackendConfig(hasServer.getConfDir());
String backendJar = backendConfig.getString("kdc_identity_backend");
File conf;
if (backendJar.equals("org.apache.kerby.has.server.kdc.MySQLIdentityBackend")) {
conf = hasServer.generateHasConf();
} else {
File confDir = hasServer.getConfDir();
conf = new File(confDir, "has-server.conf");
}
return Response.ok(conf).header("Content-Disposition", "attachment; filename=has-client.conf").build();
} catch (IOException | KrbException | HasException e) {
throw new RuntimeException("Failed to get has-client.conf. ", e);
}
}
return Response.status(403).entity("HTTPS required.\n").build();
}
/**
* Get CA file.
*
* @return Response
*/
@GET
@Path("/getcert")
@Produces(MediaType.TEXT_PLAIN)
public Response getCert() {
final HasServer hasServer = WebServer.getHasServerFromContext(context);
String errMessage = null;
File cert = null;
try {
HasConfig hasConfig = HasUtil.getHasConfig(
new File(hasServer.getConfDir(), "has-server.conf"));
if (hasConfig != null) {
String certPath = hasConfig.getSslClientCert();
cert = new File(certPath);
if (!cert.exists()) {
errMessage = "Cert file not found in HAS server.";
WebServer.LOG.error("Cert file not found in HAS server.");
}
} else {
errMessage = "has-server.conf not found.";
WebServer.LOG.error("has-server.conf not found.");
}
} catch (HasException e) {
errMessage = "Failed to get cert file" + e.getMessage();
WebServer.LOG.error("Failed to get cert file" + e.getMessage());
}
if (errMessage == null) {
return Response.ok(cert).header("Content-Disposition",
"attachment;filename=" + cert.getName()).build();
} else {
return Response.status(Response.Status.NOT_FOUND).entity(errMessage).build();
}
}
@GET
@Path("/hostroles")
@Produces(MediaType.APPLICATION_JSON + ";" + JettyUtils.UTF_8)
public Response getRoles() {
if (httpRequest.isSecure()) {
JSONArray result = new JSONArray();
try {
for (HostRoleType role : HostRoleType.values()) {
JSONObject jso = new JSONObject();
jso.put("HostRole", role.getName());
JSONArray jsa = new JSONArray();
String[] princs = role.getPrincs();
for (String princ : princs) {
jsa.put(princ);
}
jso.put("PrincipalNames", jsa);
result.put(jso);
}
return Response.ok(result.toString() + "\n").type(MediaType.APPLICATION_JSON).build();
} catch (Exception e) {
WebServer.LOG.error("Failed to get host roles." + e.getMessage());
}
return Response.serverError().build();
}
return Response.status(403).entity("HTTPS required.\n").build();
}
@GET
@Path("/kdcinit")
@Produces(MediaType.TEXT_PLAIN)
public Response kdcInit() {
if (httpRequest.isSecure()) {
final HasServer hasServer = WebServer.getHasServerFromContext(context);
try {
File adminKeytab = hasServer.initKdcServer();
return Response.ok(adminKeytab).header("Content-Disposition",
"attachment; filename=" + adminKeytab.getName()).build();
} catch (KrbException e) {
System.err.println("[ERROR] " + e.getMessage());
}
return Response.serverError().build();
}
return Response.status(403).entity("HTTPS required.\n").build();
}
@GET
@Path("/kdcstart")
@Produces(MediaType.TEXT_PLAIN)
public Response kdcStart() {
if (httpRequest.isSecure()) {
final HasServer hasServer = WebServer.getHasServerFromContext(context);
JSONObject result = new JSONObject();
String msg = "Succeed in starting KDC server.";
try {
hasServer.startKdcServer();
} catch (HasException e) {
WebServer.LOG.error("Fail to start kdc server. " + e.getMessage());
msg = e.getMessage();
}
try {
result.put("result", "success");
result.put("msg", msg);
return Response.ok(result.toString()).build();
} catch (Exception e) {
WebServer.LOG.error(e.getMessage());
msg = e.getMessage();
}
try {
result.put("result", "error");
result.put("msg", msg);
} catch (JSONException e) {
WebServer.LOG.error(e.getMessage());
}
return Response.ok(result.toString()).build();
}
return Response.status(403).entity("HTTPS required.\n").build();
}
/**
* Handle HTTP PUT request.
*/
@PUT
@Produces({MediaType.APPLICATION_OCTET_STREAM + "; " + JettyUtils.UTF_8,
MediaType.APPLICATION_JSON + "; " + JettyUtils.UTF_8})
public Response asRequest(
@QueryParam(TypeParam.NAME) @DefaultValue(TypeParam.DEFAULT)
final TypeParam type,
@QueryParam(AuthTokenParam.NAME) @DefaultValue(AuthTokenParam.DEFAULT)
final AuthTokenParam authToken
) {
return asRequest(type.getValue(), authToken.getValue());
}
private Response asRequest(String type, String tokenStr) {
if (httpRequest.isSecure()) {
final HasServer hasServer = WebServer.getHasServerFromContext(context);
String errMessage = null;
String js = null;
ObjectMapper mapper = new ObjectMapper();
final Map<String, Object> m = new TreeMap<String, Object>();
if (hasServer.getKdcServer() == null) {
errMessage = "Please start the has KDC server.";
} else if (!tokenStr.isEmpty() && tokenStr != null) {
HasKdcHandler kdcHandler = new HasKdcHandler(hasServer);
TokenDecoder tokenDecoder = KrbRuntime.getTokenProvider("JWT").createTokenDecoder();
AuthToken authToken = null;
try {
authToken = tokenDecoder.decodeFromString(tokenStr);
} catch (IOException e) {
errMessage = "Failed to decode the token string." + e.getMessage();
WebServer.LOG.error(errMessage);
}
HasServerPlugin tokenPlugin = null;
try {
tokenPlugin = HasServerPluginRegistry.createPlugin(type);
} catch (HasException e) {
errMessage = "Fail to get the plugin: " + type + ". " + e.getMessage();
WebServer.LOG.error(errMessage);
}
AuthToken verifiedAuthToken;
try {
verifiedAuthToken = tokenPlugin.authenticate(authToken);
} catch (HasAuthenException e) {
errMessage = "Failed to verify auth token: " + e.getMessage();
WebServer.LOG.error(errMessage);
verifiedAuthToken = null;
}
if (verifiedAuthToken != null) {
KrbMessage asRep = kdcHandler.getResponse(verifiedAuthToken,
(String) verifiedAuthToken.getAttributes().get("passPhrase"));
Base64 base64 = new Base64(0);
try {
m.put("type", tokenPlugin.getLoginType());
m.put("success", "true");
m.put("krbMessage", base64.encodeToString(asRep.encode()));
} catch (IOException e) {
errMessage = "Failed to encode KrbMessage." + e.getMessage();
WebServer.LOG.error(errMessage);
}
}
} else {
errMessage = "The token string should not be empty.";
WebServer.LOG.error(errMessage);
}
if (errMessage != null) {
m.put("success", "false");
m.put("krbMessage", errMessage);
}
try {
js = mapper.writeValueAsString(m);
} catch (JsonProcessingException e) {
WebServer.LOG.error("Failed write values to string." + e.getMessage());
}
return Response.ok(js).type(MediaType.APPLICATION_JSON).build();
}
return Response.status(403).entity("HTTPS required.\n").build();
}
}