/**
* personium.io
* Copyright 2014 FUJITSU LIMITED
*
* 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.fujitsu.dc.common.auth.token;
import java.io.UnsupportedEncodingException;
import org.apache.commons.lang.CharEncoding;
import org.apache.commons.lang.StringUtils;
/**
* Unit Local Unit UserToken の生成・パースを行うクラス.
*/
public class UnitLocalUnitUserToken extends AbstractOAuth2Token implements IAccessToken {
/**
* トークンのプレフィックス.
*/
public static final String PREFIX_UNIT_LOCAL_UNIT_USER = "AU~";
/**
* トークンの有効時間.
*/
public static final int ACCESS_TOKEN_EXPIRES_HOUR = 1;
/**
* AES/CBC/PKCS5Padding.
*/
private static final String SEPARATOR = "\t";
static final int IV_BYTE_LENGTH = 16;
/**
* 明示的な有効期間を設定してトークンを生成する.
* @param issuedAt 発行時刻(epochからのミリ秒)
* @param lifespan 有効時間(ミリ秒)
* @param subject ユニットユーザ名
* @param issuer 発行者(自ホスト名)
*/
public UnitLocalUnitUserToken(final long issuedAt, final long lifespan, final String subject, final String issuer) {
this.issuedAt = issuedAt;
this.lifespan = lifespan;
this.subject = subject;
this.issuer = issuer;
}
final String doCreateTokenString() {
StringBuilder raw = new StringBuilder();
// 発行時刻のEpochからのミリ秒を逆順にした文字列が先頭から入るため、推測しづらい。
String iaS = Long.toString(this.issuedAt);
String iaSr = StringUtils.reverse(iaS);
raw.append(iaSr);
raw.append(SEPARATOR);
raw.append(Long.toString(this.lifespan));
raw.append(SEPARATOR);
raw.append(this.subject);
raw.append(SEPARATOR);
raw.append(this.issuer);
return LocalToken.encode(raw.toString(), getIvBytes(issuer));
}
/**
* 指定のIssuer向けのIV(Initial Vector)を生成して返します.
* IVとしてissuerの最初の16文字を用います。 これにより、違うIssuerを想定してパースすると、パースに失敗する。
* @param issuer Issuer URL
* @return Initial Vectorの Byte配列
*/
public static byte[] getIvBytes(final String issuer) {
try {
return (issuer + "123456789abcdefg")
.substring(0, IV_BYTE_LENGTH).getBytes(CharEncoding.UTF_8);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
/**
* パース処理.
* @param token トークン
* @param issuer 発行者
* @param numFields フィールド数
* @return パースされたトークン
* @throws AbstractOAuth2Token.TokenParseException トークン解釈に失敗したとき
*/
static String[] doParse(final String token, final String issuer,
final int numFields) throws AbstractOAuth2Token.TokenParseException {
String tokenDecoded = LocalToken.decode(token, getIvBytes(issuer));
String[] frag = tokenDecoded.split(SEPARATOR);
// 正常な形式でなければパース失敗とする
if (frag.length != numFields || !issuer.equals(frag[numFields - 1])) {
throw AbstractOAuth2Token.PARSE_EXCEPTION;
}
return frag;
}
static final int IDX_COUNT = 4;
static final int IDX_ISSUED_AT = 0;
static final int IDX_LIFESPAN = 1;
static final int IDX_SUBJECT = 2;
static final int IDX_ISSUER = 3;
/**
* トークン文字列をissuerで指定されたCellとしてパースする.
* @param token Token String
* @param issuer Cell Root URL
* @return パースされたCellLocalTokenオブジェクト
* @throws AbstractOAuth2Token.TokenParseException トークンのパースに失敗したとき投げられる例外
*/
public static UnitLocalUnitUserToken parse(final String token, final String issuer)
throws AbstractOAuth2Token.TokenParseException {
if (!token.startsWith(PREFIX_UNIT_LOCAL_UNIT_USER) || issuer == null) {
throw AbstractOAuth2Token.PARSE_EXCEPTION;
}
String[] frag = doParse(token.substring(PREFIX_UNIT_LOCAL_UNIT_USER.length()), issuer, IDX_COUNT);
try {
UnitLocalUnitUserToken ret = new UnitLocalUnitUserToken(
Long.valueOf(StringUtils.reverse(frag[IDX_ISSUED_AT])),
Long.valueOf(frag[IDX_LIFESPAN]),
frag[IDX_SUBJECT],
frag[IDX_ISSUER]);
return ret;
} catch (Exception e) {
throw AbstractOAuth2Token.PARSE_EXCEPTION;
}
}
@Override
public String toTokenString() {
StringBuilder ret = new StringBuilder(PREFIX_UNIT_LOCAL_UNIT_USER);
ret.append(this.doCreateTokenString());
return ret.toString();
}
@Override
public String getTarget() {
return null;
}
@Override
public String getId() {
return this.subject + ":" + this.issuedAt;
}
}