/** * 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.core.rs.unit; import java.util.Map; import javax.ws.rs.GET; import javax.ws.rs.OPTIONS; import javax.ws.rs.Path; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import org.apache.commons.lang.NotImplementedException; import org.odata4j.core.OEntityKey; import org.odata4j.producer.EntityQueryInfo; import org.odata4j.producer.EntityResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fujitsu.dc.common.es.util.IndexNameEncoder; import com.fujitsu.dc.core.DcCoreAuthzException; import com.fujitsu.dc.core.DcCoreConfig; import com.fujitsu.dc.core.DcCoreException; import com.fujitsu.dc.core.auth.AccessContext; import com.fujitsu.dc.core.auth.OAuth2Helper.AcceptableAuthScheme; import com.fujitsu.dc.core.auth.Privilege; import com.fujitsu.dc.core.event.EventUtils; import com.fujitsu.dc.core.model.Box; import com.fujitsu.dc.core.model.BoxCmp; import com.fujitsu.dc.core.model.Cell; import com.fujitsu.dc.core.model.CellCmp; import com.fujitsu.dc.core.model.ModelFactory; import com.fujitsu.dc.core.model.file.BinaryDataAccessException; import com.fujitsu.dc.core.model.lock.UnitUserLockManager; import com.fujitsu.dc.core.odata.OEntityWrapper; import com.fujitsu.dc.core.rs.odata.ODataResource; /** * Jax-RS Resource handling DC Unit Level Api. */ public class UnitCtlResource extends ODataResource { UriInfo uriInfo; static Logger log = LoggerFactory.getLogger(UnitCtlResource.class); /** * beforeDelete時にCellの検索をした結果をafterDeleteで利用するためのキャッシュ. */ Cell cell; /** * コンストラクタ. * @param accessContext AccessContext * @param uriInfo UriInfo */ public UnitCtlResource(AccessContext accessContext, UriInfo uriInfo) { super(accessContext, uriInfo.getBaseUri().toASCIIString() + "__ctl/", ModelFactory.ODataCtl.unitCtl(accessContext)); this.uriInfo = uriInfo; checkReferenceMode(accessContext); } private void checkReferenceMode(AccessContext accessContext) { String unitUserName = accessContext.getSubject(); String unitPrefix = DcCoreConfig.getEsUnitPrefix(); if (unitUserName == null) { unitUserName = "anon"; } else { unitUserName = IndexNameEncoder.encodeEsIndexName(unitUserName); } if (UnitUserLockManager.hasLockObject(unitPrefix + "_" + unitUserName)) { throw DcCoreException.Server.SERVICE_MENTENANCE_RESTORE; } } @Override public void checkAccessContext(AccessContext ac, Privilege privilege) { // ユニットマスター、ユニットユーザ、ユニットローカルユニットユーザなら受け付ける if (AccessContext.TYPE_UNIT_MASTER.equals(ac.getType())) { return; } else if (AccessContext.TYPE_UNIT_USER.equals(ac.getType())) { return; } else if (AccessContext.TYPE_UNIT_LOCAL.equals(ac.getType())) { return; } else if (AccessContext.TYPE_INVALID.equals(ac.getType())) { ac.throwInvalidTokenException(getAcceptableAuthScheme()); } else if (AccessContext.TYPE_ANONYMOUS.equals(ac.getType())) { throw DcCoreAuthzException.AUTHORIZATION_REQUIRED.realm(ac.getRealm(), getAcceptableAuthScheme()); } // ユニットマスター、ユニットユーザ、ユニットローカルユニットユーザ以外なら権限エラー throw DcCoreException.Auth.UNITUSER_ACCESS_REQUIRED; } /** * 認証に使用できるAuth Schemeを取得する. * @return 認証に使用できるAuth Scheme */ @Override public AcceptableAuthScheme getAcceptableAuthScheme() { return AcceptableAuthScheme.BEARER; } @Override public boolean hasPrivilege(AccessContext ac, Privilege privilege) { return false; } @Override public void checkSchemaAuth(AccessContext ac) { } @Override public void beforeCreate(final OEntityWrapper oEntityWrapper) { if (AccessContext.TYPE_UNIT_USER.equals(this.getAccessContext().getType()) || AccessContext.TYPE_UNIT_LOCAL.equals(this.getAccessContext().getType())) { // ユニットユーザトークンでSubjectの値がある場合はその値にする。 String subject = this.getAccessContext().getSubject(); if (subject != null) { oEntityWrapper.put("Owner", subject); } } } /** * 更新時に必要なチェック処理. * @param oEntityWrapper OEntityWrapper * @param oEntityKey 更新対象のentityKey */ @Override public void beforeUpdate(final OEntityWrapper oEntityWrapper, final OEntityKey oEntityKey) { String entitySetName = oEntityWrapper.getEntitySet().getName(); EntityResponse er = this.getODataProducer() .getEntity(entitySetName, oEntityKey, new EntityQueryInfo.Builder().build()); OEntityWrapper oew = (OEntityWrapper) er.getEntity(); // エンティティごとのアクセス可否判断 this.checkAccessContextPerEntity(this.getAccessContext(), oew); } @Override public void beforeDelete(final String entitySetName, final OEntityKey oEntityKey) { EntityResponse er = this.getODataProducer() .getEntity(entitySetName, oEntityKey, new EntityQueryInfo.Builder().build()); OEntityWrapper oew = (OEntityWrapper) er.getEntity(); // エンティティごとのアクセス可否判断 this.checkAccessContextPerEntity(this.getAccessContext(), oew); if (Cell.EDM_TYPE_NAME.equals(entitySetName)) { String cellId = oew.getUuid(); cell = ModelFactory.cell(cellId, uriInfo); // Cell配下が空っぽじゃなければ409エラー if (!cell.isEmpty()) { throw DcCoreException.OData.CONFLICT_HAS_RELATED; } } } @Override public void afterDelete(final String entitySetName, final OEntityKey oEntityKey) { if (Cell.EDM_TYPE_NAME.equals(entitySetName)) { // Cell配下にイベントログが存在する場合は削除 String owner = cell.getOwner(); try { EventUtils.deleteEventLog(this.cell.getId(), owner); } catch (BinaryDataAccessException e) { log.warn("Failed to delete eventlog. CellName=[" + this.cell.getName() + "] owner=[" + owner + "] " + e.getMessage()); } // delete Main Box DavCmp Box box = new Box(this.cell, null); BoxCmp boxCmp = ModelFactory.boxCmp(box); boxCmp.delete(null, false); // delete cell DavCmp CellCmp cellCmp = ModelFactory.cellCmp(this.cell); cellCmp.delete(null, false); } } /** * サービスメタデータリクエストに対応する. * @return JAX-RS 応答オブジェクト */ @GET @Path("{first: \\$}metadata") public Response getMetadata() { return super.doGetMetadata(); } /** * OPTIONSメソッド. * @return JAX-RS Response */ @OPTIONS @Path("{first: \\$}metadata") public Response optionsMetadata() { return super.doGetOptionsMetadata(); } @Override public void checkAccessContextPerEntity(AccessContext ac, OEntityWrapper oew) { Map<String, Object> meta = oew.getMetadata(); String owner = (String) meta.get("Owner"); // マスタートークンだったらチェック不要 if (AccessContext.TYPE_UNIT_MASTER.equals(ac.getType())) { return; } // ユニットユーザトークン、ユニットローカルユニットユーザトークンではオーナーが同じセルに対してのみ操作を許す. // Ownerが空の場合はマスタートークン以外許さない if (owner == null || !owner.equals(ac.getSubject())) { throw DcCoreException.Auth.NOT_YOURS; } } @Override public Privilege getNecessaryReadPrivilege(String entitySetNameStr) { // UnitレベルにはPrivilegeを設定できない仕様のためnullを返す。そもそもこの関数呼ぶことはない。 return null; } @Override public Privilege getNecessaryWritePrivilege(String entitySetNameStr) { // UnitレベルにはPrivilegeを設定できない仕様のためnullを返す。そもそもこの関数呼ぶことはない。 return null; } @Override public Privilege getNecessaryOptionsPrivilege() { // UnitレベルにはPrivilegeを設定できない仕様のためnullを返す。そもそもこの関数呼ぶことはない。 return null; } @Override public void setBasicAuthenticateEnableInBatchRequest(AccessContext ac) { // UnitレベルAPIはバッチリクエストに対応していないため、ここでは何もしない } /** * Not Implemented. <br /> * 現状、$batchのアクセス制御でのみ必要なメソッドのため未実装. <br /> * アクセスコンテキストが$batchしてよい権限を持っているかを返す. * @param ac アクセスコンテキスト * @return true: アクセスコンテキストが$batchしてよい権限を持っている */ @Override public boolean hasPrivilegeForBatch(AccessContext ac) { throw new NotImplementedException(); } }