org.apigw.commons.logging.logback.ApigwStripSwedishSSNFromMessageConverter.java Source code

Java tutorial

Introduction

Here is the source code for org.apigw.commons.logging.logback.ApigwStripSwedishSSNFromMessageConverter.java

Source

/**
 *   Copyright 2013 Stockholm County Council
 *
 *   This file is part of APIGW
 *
 *   APIGW is free software; you can redistribute it and/or modify
 *   it under the terms of version 2.1 of the GNU Lesser General Public
 *   License as published by the Free Software Foundation.
 *
 *   APIGW is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public
 *   License along with APIGW; if not, write to the
 *   Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 *   Boston, MA 02111-1307  USA
 *
 */

package org.apigw.commons.logging.logback;

import ch.qos.logback.classic.pattern.ClassicConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by martin on 17/12/14.
 *
 * Will first detect possible Swedish personnummer or samordningsnummer and, if anything found,
 * check the control number to see if it might be valid and if so, check the birth date against today.
 *
 * If found to be a possible valid SSN, replace it with replacement string
 *
 * Might result in false positives
 *
 * Handles birth years from 1800 to 2199, if this code lives beyond that, GREAT!
 */
public class ApigwStripSwedishSSNFromMessageConverter extends ClassicConverter {

    private static final Pattern ssnPattern = Pattern.compile(
            "\\b(((1[89]{1})|(2[01]{1}))[0-9]{2}((0[1-9]{1})|(1[0-2]{1}))(([06]{1}[1-9]{1})|([1278]{1}[0-9]{1})|([39]{1}[01]{1}))[-]?[0-9]{4})\\b");
    private static final DateTimeFormatter yearMonthDayFormat = DateTimeFormat.forPattern("yyyyMMdd");
    private String replacement = "***"; //default to ***

    @Override
    public void start() {
        String firstOption = getFirstOption();
        if (firstOption != null && !firstOption.isEmpty()) {
            replacement = firstOption;
        }
        super.start();
    }

    @Override
    public void stop() {
        replacement = null;
        super.stop();
    }

    @Override
    public String convert(ILoggingEvent event) {
        String formattedMessage = event.getFormattedMessage();
        Matcher matcher = ssnPattern.matcher(formattedMessage);

        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            String possibleSsn = matcher.group(0);
            boolean validSsn = isValidSsn(possibleSsn);
            matcher.appendReplacement(sb, validSsn ? replacement : possibleSsn);
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    private static boolean isValidSsn(final String ssn) {
        String controlSsn;
        if (ssn.contains("-")) {
            controlSsn = ssn.replace("-", "");
        } else {
            controlSsn = ssn;
        }
        int controlSsnLength = controlSsn.length();
        return (controlSsnLength == 10 || controlSsnLength == 12) && isSsnControlValid(controlSsn)
                && isSsnBirthdayInThePast(controlSsn);
    }

    private static boolean isSsnControlValid(String ssn) {
        String tenDigitSsn = ssn.substring(2);
        int[] digits = new int[10];
        for (int i = 0; i < tenDigitSsn.length(); i += 1) {
            digits[i] = Integer.parseInt(String.valueOf(tenDigitSsn.charAt(i)));
        }
        return digits[digits.length - 1] == calculateControl(digits);
    }

    private static int calculateControl(int[] ssnArr) {
        int checkSum = 0;
        int temp = 0;
        int controlNumber;
        for (int i = 0; i < 9; i += 1) {
            if (i % 2 == 0) {
                temp = 2 * ssnArr[i];
                if (temp >= 10) {
                    checkSum = checkSum + 1 + (temp - 10);
                } else {
                    checkSum = checkSum + temp;
                }
            } else {
                checkSum = checkSum + ssnArr[i];
            }
        }
        controlNumber = 10 - checkSum % 10;
        if (controlNumber == 10) {
            controlNumber = 0;
        }
        return controlNumber;
    }

    private static boolean isSsnBirthdayInThePast(String ssn) {
        return !getBirthday(ssn).isAfter(LocalDate.now());
    }

    private static LocalDate getBirthday(String ssn) {
        int firstDayNumber = Integer.parseInt(ssn.substring(6, 7));
        String yearMonthDay;
        if (firstDayNumber >= 6) { //Samordningsnummer, need to lessen the day of month by 60
            firstDayNumber = firstDayNumber - 6;
            yearMonthDay = ssn.substring(0, 6) + firstDayNumber + ssn.substring(7, 8);
        } else {
            yearMonthDay = ssn.substring(0, 8);
        }
        return LocalDate.parse(yearMonthDay, yearMonthDayFormat);
    }
}