/** * 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.client; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.util.HashMap; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.commons.codec.binary.Base64; import org.apache.http.Header; import org.apache.http.HttpStatus; import org.json.simple.JSONObject; import org.w3c.dom.Document; import org.xml.sax.SAXException; import com.fujitsu.dc.client.http.BatchAdapter; import com.fujitsu.dc.client.http.DcResponse; import com.fujitsu.dc.client.http.RestAdapter; import com.fujitsu.dc.client.http.RestAdapterFactory; import com.fujitsu.dc.client.utils.UrlUtils; ///** // * アクセッッサクラス. personium.ioへアクセスするAPIを呼び出す際のアクセス主体となります。 // */ /** * It creates a new object of Accessor. This is the base class for setting the access parameters to access Cloud data. */ public class Accessor implements Cloneable { // /// Accessor 内部で使用するフィールド // /** asメソッドに利用する type. */ /** Final variable for holding key for SELF. */ public static final String KEY_SELF = "self"; // /** asメソッドに利用する type. */ /** Final variable for holding key for CLIENT. */ public static final String KEY_CLIENT = "client"; // /** asメソッドに利用する type. */ /** Final variable for holding key for TOKEN. */ public static final String KEY_TOKEN = "token"; // /// 認証してもらった情報を保持するフィールド // /** トークンの有効期限. */ /** Expiration date of the token. */ private Number expiresIn = null; // /** アクセストークン. */ /** Access token. */ private String accessToken = null; // /** リフレッシュトークンの有効期限. */ /** Expiration date of the refresh token. */ private Number refreshExpiresIn = null; // /** リフレッシュトークン. */ /** Refresh Token. */ private String refreshToken = null; // /** トークンタイプ. */ /** Token type. */ private String tokenType = null; /** Parameter to represent level of schema authorization. */ private JSONObject schemaAuth; // /// クライアントから渡されるフィールド // /** "self","client"等のタイプを保持. */ /** Holds the type of self "," client ", etc. */ private String accessType = ""; // /** Cellの名前. */ /** Authorised cell URL. */ private String authCellUrl; // /** 認証ID. */ /** Authentication User ID. */ private String userId; // /** 認証パスワード. */ /** Authentication password. */ private String password; // /** スキーマ. */ /** Schema. */ private String schema; // /** スキーマ認証ID. */ /** Schema authentication ID. */ private String schemaUserId; // /** スキーマ認証パスワード. */ /** Schema authentication password. */ private String schemaPassword; // /** 対象Cellの名前. */ /** Cell name. */ private String targetCellName; // /** トランスセルトークン. */ /** Transformer cell token. */ private String transCellToken; // /** トランスセルリフレッシュトークン. */ /** Transformer cell refresh token. */ private String transCellRefreshToken; // /** オーナー. */ /** Owner. */ protected boolean owner = false; // ///// // /** 現在のBox Schema. */ /** Current Box Schema. */ private String boxSchema = ""; // /** 現在のBox Name. */ /** Current Box Name. */ private String boxName = ""; // /** 基底URL. */ /** Base URL. */ private String baseUrl = ""; // /** DCコンテキスト. */ /** Reference to DcContext. */ private DcContext context; /** Cell. */ private Cell currentCell; // /** バッチモード. */ /** Batch Mode. */ private boolean batch; /** BatchAdapter. */ private BatchAdapter batchAdapter; // /** デフォルトヘッダ. */ /** Default header. */ HashMap<String, String> defaultHeaders; // /** サーバーのレスポンスから取得したレスポンスヘッダ. */ /** Response header acquired from the server response. */ private HashMap<String, String> resHeaders = new HashMap<String, String>(); // /** // * コンストラクタ. // * @param dcContext DCコンテキスト // */ /** * This is the parameterized constructor initializing the various class variables. * @param dcContext DcContext */ public Accessor(DcContext dcContext) { this.expiresIn = 0; this.refreshExpiresIn = 0; this.context = dcContext; this.baseUrl = dcContext.getBaseUrl(); this.authCellUrl = dcContext.getCellName(); this.boxSchema = dcContext.getBoxSchema(); this.boxName = dcContext.getBoxName(); } // /** // * Accessorのクローンを生成する. // * @return コピーしたAccessorオブジェクト // */ /** * This method is used to initialize the class variable accessor. * @return Accessor object */ public Accessor clone() { try { return (Accessor) super.clone(); } catch (CloneNotSupportedException e) { return null; } } // /** // * Cell を指定します. // * @return CellへアクセスするためのCellインスタンス // * @throws DaoException DAO例外 // */ /** * This method returns the specified cell. It does not take any parameter. * @return Cell object * @throws DaoException Exception thrown */ public Cell cell() throws DaoException { return this.cell(this.getCellName()); } // /** // * 他のCellを指定します. // * @param cell 接続先Cell URL // * @return CellへアクセスするためのCellインスタンス // * @throws DaoException DAO例外 // */ /** * This method returns the cell specified in the parameter. * @param cell Destination Cell URL * @return Cell instance * @throws DaoException Exception thrown */ public Cell cell(String cell) throws DaoException { if (!this.authCellUrl.equals(cell)) { this.targetCellName = cell; } // Unit昇格時はこのタイミングで認証を行わない /** Authentication is not performed. */ if (!this.owner) { certification(); } return new Cell(this, cell); } // /** // * パスワード変更. // * @param newPassword 変更するパスワード // * @throws DaoException DAO例外 // */ /** * This method changes the current password. * @param newPassword Password new value * @throws DaoException Exception thrown */ public void changePassword(String newPassword) throws DaoException { if (this.accessToken == null) { // accessTokenが無かったら自分で認証する /** authentication is performed when accessToken is not present. */ certification(); } // パスワード変更 /** Password change. */ HashMap<String, String> headers = new HashMap<String, String>(); headers.put("X-Dc-Credential", newPassword); // パスワード変更のURLを作成 /** Create the URL for password change. */ String cellUrl = this.getCellName(); if (!UrlUtils.isUrl(cellUrl)) { cellUrl = UrlUtils.append(this.getBaseUrl(), this.getCellName()); } String url = UrlUtils.append(cellUrl, "__mypassword"); RestAdapter rest = (RestAdapter) RestAdapterFactory.create(this); rest.put(url, headers); // password変更でエラーの場合は例外がthrowされるので例外で無い場合は、 // Accessorのpasswordを新しいのに変えておく this.password = newPassword; } // /** // * $Batchモードの取得. // * @return batchモード // */ /** * This method returns $Batch - batch mode. * @return batch mode */ public final boolean isBatchMode() { return batch; } // /** // * $Batchモードの設定. // * @param batch $Batchモード // */ /** * This method sets $Batch - batch mode. * @param batch mode */ public final void setBatch(boolean batch) { this.batchAdapter = new BatchAdapter(this); this.batch = batch; } // /** // * BatchAdaptrの取得. インスタンスが生成されていない場合生成する // * @return BatchAdapterオブジェクト // */ /** * This method generates BatchAdapter instance if not created before. * @return BatchAdapter object */ public BatchAdapter getBatchAdapter() { if (null == this.batchAdapter) { this.batchAdapter = new BatchAdapter(this); } return this.batchAdapter; } // /** // * Unit昇格. // * @return 昇格後のAccessor(OwnerAccessor) // * @throws DaoException DAO例外 // */ /** * This method performs Unit Promotion by creating and initializing OwnerAccessor. * @return Promoted OwnerAccessor * @throws DaoException Exception thrown */ public OwnerAccessor asCellOwner() throws DaoException { OwnerAccessor oas; oas = new OwnerAccessor(this.context, this); oas.defaultHeaders = this.defaultHeaders; return oas; } // /** // * グローバルトークンの取得. // * @return グローバルトークン // */ /** * This method returns the global access token. * @return global token */ public String getAccessToken() { return this.accessToken; } // /** // * デフォルトヘッダを設定. // * @param value デフォルトヘッダ // */ /** * This method sets the default header. * @param value Default header Map */ public void setDefaultHeaders(HashMap<String, String> value) { this.defaultHeaders = value; } // /** // * デフォルトヘッダを取得. // * @return デフォルトヘッダ // */ /** * This method gets the default header. * @return Default header */ public HashMap<String, String> getDefaultHeaders() { return this.defaultHeaders; } // /** // * グローバルトークンの設定. // * @param token グローバルトークン // */ /** * This method sets the global access token. * @param token Access Token */ void setAccessToken(String token) { this.accessToken = token; } // /** // * DaoConfigオブジェクトを取得. // * @return DaoConfigオブジェクト // */ /** * This method gets the DaoConfig object. * @return DaoConfig object */ public DaoConfig getDaoConfig() { return context.getDaoConfig(); } // /** // * DCコンテキストの取得. // * @return DCコンテキスト // */ /** * This method returns the DcContext object. * @return DcContext */ DcContext getContext() { return this.context; } // /** // * DCコンテキストの設定. // * @param c DCコンテキスト // */ /** * This method sets the DcContext object. * @param c DcCOntext */ void setContext(DcContext c) { this.context = c; } // /** // * 現在アクセス中のCell取得. // * @return Cellクラスインスタンス // */ /** * This method returns the current cell being accessed. * @return Current cell object */ Cell getCurrentCell() { return this.currentCell; } // /** // * 現在アクセス中のCell設定. // * @param cell Cellクラスインスタンス // */ /** * This method sets the current cell as specified. * @param cell Cell object */ void setCurrentCell(Cell cell) { this.currentCell = cell; } // /** // * トークンの有効期限の取得. // * @return トークンの有効期限 // */ /** * This method returns the expiration value of token. * @return Expiration date of the token */ public Number getExpiresIn() { return this.expiresIn; } // /** // * リフレッシュトークンの設定. // * @return リフレッシュトークン // */ /** * This method returns the refresh token value. * @return Refresh token value */ public String getRefreshToken() { return refreshToken; } // /** // * リフレッシュトークンの設定. // * @return リフレッシュトークン // */ /** * This method returns the token type. * @return token type */ public String getTokenType() { return tokenType; } // /** // * リフレッシュトークンの有効期限の取得. // * @return リフレッシュトークンの有効期限 // */ /** * This method returns the expiration date of refresh token. * @return expiration date of refresh token */ public Number getRefreshExpiresIn() { return refreshExpiresIn; } // /** // * CellName値の取得. // * @return CellName値 // */ /** * This method returns the authorized cell URL. * @return CellName URL */ String getCellName() { return this.authCellUrl; } // /** // * CellName値の設定. // * @param name CellName値 // */ /** * This method sets the CellName value. * @param name CellName value */ void setCellName(String name) { this.authCellUrl = name; } // /** // * Box Schemaの取得. // * @return Schema名 // */ /** * This method gets the Box Schema value. * @return BoxSchema value */ String getBoxSchema() { return this.boxSchema; } // /** // * Box Schemaの設定. // * @param uri Box Schema名 // */ /** * This method sets the Box Schema value. * @param uri Box Schema value */ void setBoxSchema(String uri) { this.boxSchema = uri; } // /** // * Box Nameを取得. // * @return Box Name // */ /** * This method returns theBox Name value. * @return Box Name */ String getBoxName() { return this.boxName; } // /** // * Box Nameの設定. // * @param value Box Name // */ /** * This method sets the Box Name value. * @param value Box Name */ void setBoxName(String value) { this.boxName = value; } // /** // * 基底URLを設定. // * @return URL文字列 // */ /** * This method gets the base URL value. * @return BaseURL value */ String getBaseUrl() { return this.baseUrl; } // /** // * 基底URLを取得. // * @param value URL文字列 // */ /** * This method sets the base URL value. * @param value BaseURL value */ void setBaseUrl(String value) { this.baseUrl = value; } /** * This method returns the userId. * @return the userId */ String getUserId() { return userId; } /** * This method sets the userId. * @param userId the userId to set */ void setUserId(String userId) { this.userId = userId; } /** * This method retruns the password. * @return the password */ String getPassword() { return password; } /** * This method sets the password. * @param password the password to set */ void setPassword(String password) { this.password = password; } /** * This method returns the schema. * @return the schema */ String getSchema() { return schema; } /** * This method sets the schema. * @param schema the schema to set */ void setSchema(String schema) { this.schema = schema; } /** * This method returns the target cell name. * @return the targetCellName */ String getTargetCellName() { return targetCellName; } /** * This method sets the target cell name. * @param targetCellName the targetCellName to set */ void setTargetCellName(String targetCellName) { this.targetCellName = targetCellName; } /** * This method sets the access type. * @param accessType the accessType to set */ public void setAccessType(String accessType) { this.accessType = accessType; } // /** // * "self","client"等のタイプを返却. // * @return タイプ // */ /** * This method gets the access type "self", "client", etc. * @return access type */ String getAccessType() { return this.accessType; } /** * This method sets the transformer cell token. * @param transCellToken the trancCellToken to set */ void setTransCellToken(String trancCellToken) { this.transCellToken = trancCellToken; } /** * This method gets the transformer cell token. * @param the transCellToken */ String getTransCellToken() { return this.transCellToken; } /** * This method sets the transformer cell refresh token. * @param trancCellRefreshToken the trancCellRefreshToken to set */ void setTransCellRefreshToken(String trancCellRefreshToken) { this.transCellRefreshToken = trancCellRefreshToken; } /** * This method gets the transformer cell refresh token. * @param the trancCellRefreshToken */ String getTransCellRefreshToken() { return this.transCellRefreshToken; } /** * This method sets the schema user ID. * @param schemaUserId the schemaUserId to set */ void setSchemaUserId(String schemaUserId) { this.schemaUserId = schemaUserId; } /** * This method gets the schema user ID. * @param the schemaUserId */ String getSchemaUserId() { return this.schemaUserId; } /** * This method sets the schema password. * @param schemaPassword the schemaPassword to set */ void setSchemaPassword(String schemaPassword) { this.schemaPassword = schemaPassword; } /** * This method gets the schema password. * @param the schemaPassword */ String getSchemaPassword() { return this.schemaPassword; } // /** // * サーバーのレスポンスから取得したレスポンスヘッダを設定. // * @param headers 設定するヘッダ // */ /** * This method sets the response headers that are retrieved from the server response. * @param headers Response headers set */ public void setResHeaders(Header[] headers) { for (Header header : headers) { this.resHeaders.put(header.getName(), header.getValue()); if (header.getName().equals(DcContext.DC_VERSION)) { this.context.setServerVersion(header.getValue()); } } } // /** // * レスポンスヘッダの取得. // * @return レスポンスヘッダの一覧 // */ /** * This method gets the response headers retrieved from the server response. * @return Response Headers */ public HashMap<String, String> getResHeaders() { return this.resHeaders; } // /** // * 認証を行う. // * @throws DaoException DAO例外 // */ /** * This method performs authentication of user credentails based on token type etc. * @throws DaoException Exception thrown */ protected void certification() throws DaoException { // アクセスタイプがselfかクライアントの場合は、認証処理は行わない /** If the access type of client or self, then authentication process is not performed. */ if (this.accessType.equals(Accessor.KEY_CLIENT) || this.accessType.equals(Accessor.KEY_SELF) || this.accessType.equals(Accessor.KEY_TOKEN)) { return; } RestAdapter rest = (RestAdapter) RestAdapterFactory.create(this); // 認証するurlを作成する /** Create a url to authenticate. */ String authUrl = createCertificatUrl(); // 認証するためのリクエストボディを作る /** Make a request body to authenticate. */ StringBuilder requestBody = new StringBuilder(); if (this.transCellToken != null) { // トランスセルトークン認証 /** Transformer cell token authentication. */ requestBody.append("grant_type=urn:ietf:params:oauth:grant-type:saml2-bearer&assertion="); requestBody.append(this.transCellToken); } else if (this.transCellRefreshToken != null) { // リフレッシュトークン認証 /** Refresh token authentication. */ requestBody.append("grant_type=refresh_token&refresh_token="); requestBody.append(this.transCellRefreshToken); } else if (userId != null) { // パスワード認証 /** Password authentication. */ requestBody.append("grant_type=password&username="); requestBody.append(this.userId); requestBody.append("&password="); requestBody.append(this.password); } // targetのURLを作る /** Create Target URL. */ if (this.targetCellName != null) { requestBody.append("&dc_target="); if (UrlUtils.isUrl(this.targetCellName)) { requestBody.append(this.targetCellName); } else { requestBody.append(UrlUtils.append(baseUrl, this.targetCellName)); } } // スキーマ付き認証のためにスキーマ情報を付加する /** Add the schema information for authentication schema. */ if (this.schemaUserId != null && this.schemaPassword != null) { // スキーマ認証 /** Authentication schema. */ StringBuilder schemaRequestBody = new StringBuilder(); schemaRequestBody.append("grant_type=password&username="); schemaRequestBody.append(this.schemaUserId); schemaRequestBody.append("&password="); schemaRequestBody.append(this.schemaPassword); schemaRequestBody.append("&dc_target="); schemaRequestBody.append(authUrl); // Urlでない場合は、BaseURLにスキーマ名を足す /** If this is not the Url, add the schema name to BaseURL. */ if (!UrlUtils.isUrl(this.schema)) { this.schema = UrlUtils.append(baseUrl, this.schema); } if (!this.schema.endsWith("/")) { this.schema += "/"; } DcResponse res = rest.post(UrlUtils.append(this.schema, "__auth"), schemaRequestBody.toString(), RestAdapter.CONTENT_FORMURLENCODE, false); this.schemaAuth = res.bodyAsJson(); requestBody.append("&client_id="); requestBody.append(this.schema); requestBody.append("&client_secret="); requestBody.append((String) this.schemaAuth.get("access_token")); } if (owner) { requestBody.append("&dc_owner=true"); } // 認証してトークンを保持する /** To hold the token to authenticate. */ DcResponse res = rest.post(UrlUtils.append(authUrl, "__auth"), requestBody.toString(), RestAdapter.CONTENT_FORMURLENCODE, false); JSONObject json = res.bodyAsJson(); this.accessToken = (String) json.get("access_token"); this.expiresIn = (Number) json.get("expires_in"); this.refreshToken = (String) json.get("refresh_token"); this.refreshExpiresIn = (Number) json.get("refresh_token_expires_in"); this.tokenType = (String) json.get("token_type"); } // /** // * レスポンスボディをXMLで取得. // * @return XML DOMオブジェクト // */ /** * This method returns the transformer cell token as XML. * @return XML DOM object */ private Document trancCellTokenAsXml() throws DaoException { String saml; try { saml = new String(Base64.decodeBase64(this.transCellToken), "UTF-8"); } catch (UnsupportedEncodingException e) { throw DaoException.create(e.getMessage(), HttpStatus.SC_BAD_REQUEST); } DocumentBuilder builder = null; try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); builder = factory.newDocumentBuilder(); } catch (ParserConfigurationException e) { throw new RuntimeException(e); } Document document = null; InputStream is = new ByteArrayInputStream(saml.getBytes()); try { document = builder.parse(is); } catch (SAXException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } return document; } // /** // * 認証先Cellのburlを作成する. // * @return 認証先Cellのurl // * @throws DaoException // */ /** * This method creates a url of authentication destination Cell. * @return authentication destination Cell URL * @throws DaoException Exception thrown */ private String createCertificatUrl() throws DaoException { // 認証するurlを作成する /** Create a url to authenticate. */ String authUrl; if (this.transCellToken != null) { // トークンから接続先urlを取得する /** Get the connection destination url from the token. */ Document doc = trancCellTokenAsXml(); authUrl = doc.getElementsByTagName("Audience").item(0).getFirstChild().getNodeValue(); } else if (UrlUtils.isUrl(this.authCellUrl)) { authUrl = this.authCellUrl; } else { authUrl = UrlUtils.append(baseUrl, this.authCellUrl); } return authUrl; } // /** // * Cellへのグローバルトークンを取得する. // * @return トークン // */ /** * This method gets the global token of the cell. * @return token Empty string */ protected String loadGlobalToken() { return ""; } // /** // * Boxへのローカルトークンを取得する. // * @return トークン // */ /** * This method gets the local token for the box. * @return token Empty string */ protected String loadClientToken() { return ""; } }