/**
* Copyright 2014 Sean Kavanagh - sean.p.kavanagh6@gmail.com
*
* Licensed 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 com.keybox.manage.action;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.keybox.common.util.AppConfig;
import com.keybox.common.util.AuthUtil;
import com.keybox.manage.db.AuthDB;
import com.keybox.manage.db.UserDB;
import com.keybox.manage.util.OTPUtil;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.InterceptorRef;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.interceptor.ServletResponseAware;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.net.URLEncoder;
import java.util.Date;
import java.util.Hashtable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@InterceptorRef("keyboxStack")
public class OTPAction extends ActionSupport implements ServletRequestAware, ServletResponseAware {
private static Logger log = LoggerFactory.getLogger(OTPAction.class);
public final static boolean requireOTP = "required".equals(AppConfig.getProperty("oneTimePassword"));
//QR image size
private static final int QR_IMAGE_WIDTH = 325;
private static final int QR_IMAGE_HEIGHT = 325;
HttpServletRequest servletRequest;
HttpServletResponse servletResponse;
String qrImage;
String sharedSecret;
@Action(value = "/admin/viewOTP",
results = {
@Result(name = "success", location = "/admin/two-factor_otp.jsp"),
@Result(name = "error", location = "/login.action", type = "redirect")
}
)
public String viewOTP() {
sharedSecret = OTPUtil.generateSecret();
AuthUtil.setOTPSecret(servletRequest.getSession(), sharedSecret);
this.setQrImage(Long.toString(new Date().getTime()) + ".png");
return SUCCESS;
}
@Action(value = "/admin/otpSubmit",
results = {
@Result(name = "success", location = "/logout.action", type = "redirect")
}
)
public String otpSubmit() {
AuthDB.updateSharedSecret(sharedSecret, AuthUtil.getAuthToken(servletRequest.getSession()));
if (requireOTP) {
AuthUtil.deleteAllSession(servletRequest.getSession());
}
return SUCCESS;
}
@Action(value = "/admin/qrImage")
public String qrImage() {
String username = UserDB.getUser(AuthUtil.getUserId(servletRequest.getSession())).getUsername();
String secret = AuthUtil.getOTPSecret(servletRequest.getSession());
AuthUtil.setOTPSecret(servletRequest.getSession(), null);
try {
String qrCodeText = "otpauth://totp/KeyBox%20%28" + URLEncoder.encode(servletRequest.getHeader("host").replaceAll("\\:.*$",""), "utf-8") + "%29:" + username + "?secret=" + secret;
QRCodeWriter qrWriter = new QRCodeWriter();
Hashtable<EncodeHintType, String> hints = new Hashtable<>();
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
BitMatrix matrix = qrWriter.encode(qrCodeText, BarcodeFormat.QR_CODE, QR_IMAGE_WIDTH, QR_IMAGE_HEIGHT, hints);
servletResponse.setContentType("image/png");
BufferedImage image = new BufferedImage(QR_IMAGE_WIDTH, QR_IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = (Graphics2D) image.getGraphics();
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, QR_IMAGE_WIDTH, QR_IMAGE_HEIGHT);
graphics.setColor(Color.BLACK);
for (int x = 0; x < QR_IMAGE_WIDTH; x++) {
for (int y = 0; y < QR_IMAGE_HEIGHT; y++) {
if (matrix.get(x, y)) {
graphics.fillRect(x, y, 1, 1);
}
}
}
ImageIO.write(image, "png", servletResponse.getOutputStream());
servletResponse.getOutputStream().flush();
servletResponse.getOutputStream().close();
} catch (Exception ex) {
log.error(ex.toString(), ex);
}
return null;
}
public String getQrImage() {
return qrImage;
}
public void setQrImage(String qrImage) {
this.qrImage = qrImage;
}
public HttpServletResponse getServletResponse() {
return servletResponse;
}
public void setServletResponse(HttpServletResponse servletResponse) {
this.servletResponse = servletResponse;
}
public HttpServletRequest getServletRequest() {
return servletRequest;
}
public void setServletRequest(HttpServletRequest servletRequest) {
this.servletRequest = servletRequest;
}
public String getSharedSecret() {
return sharedSecret;
}
public void setSharedSecret(String sharedSecret) {
this.sharedSecret = sharedSecret;
}
}