All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.centurylink.mdw.services.rest.RestService Maven / Gradle / Ivy

There is a newer version: 6.1.39
Show newest version
/*
 * Copyright (C) 2017 CenturyLink, Inc.
 *
 * 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.centurylink.mdw.services.rest;

import com.centurylink.mdw.common.service.AuthorizationException;
import com.centurylink.mdw.common.service.Query;
import com.centurylink.mdw.common.service.ServiceException;
import com.centurylink.mdw.config.PropertyManager;
import com.centurylink.mdw.model.JsonObject;
import com.centurylink.mdw.model.listener.Listener;
import com.centurylink.mdw.model.user.Role;
import com.centurylink.mdw.model.user.User;
import com.centurylink.mdw.model.user.UserAction;
import com.centurylink.mdw.model.user.UserAction.Action;
import com.centurylink.mdw.model.user.UserAction.Entity;
import com.centurylink.mdw.model.user.Workgroup;
import com.centurylink.mdw.service.data.user.UserGroupCache;
import com.centurylink.mdw.services.ServiceLocator;
import com.centurylink.mdw.util.log.LoggerUtil;
import com.centurylink.mdw.util.log.StandardLogger;
import org.apache.commons.lang.StringUtils;
import org.json.JSONObject;

import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public abstract class RestService {

    public static final int HTTP_200_OK = 200;
    public static final int HTTP_201_CREATED = 201;
    public static final int HTTP_202_ACCEPTED = 202;

    public static final int HTTP_400_BAD_REQUEST = 400;
    public static final int HTTP_401_UNAUTHORIZED = 401;
    public static final int HTTP_403_FORBIDDEN = 403;
    public static final int HTTP_404_NOT_FOUND = 404;
    public static final int HTTP_405_METHOD_NOT_ALLOWED = 405;
    public static final int HTTP_409_CONFLICT = 409;

    public static final int HTTP_500_INTERNAL_ERROR = 500;
    public static final int HTTP_501_NOT_IMPLEMENTED = 501;

    private static StandardLogger logger = LoggerUtil.getStandardLogger();

    /**
     * The user identified in the AuthenticatedUser headers must be authorized to
     * perform this action.  For HTTP, MDW removes this header (if it exists) from
     * every request and then populates based on session authentication or HTTP Basic.
     * @param content
     *
     * @return authorized user if successful
     */
    protected User authorize(String path, JSONObject content, Map headers)
    throws AuthorizationException {
        User user = null;
        String userId = headers.get(Listener.AUTHENTICATED_USER_HEADER);
        String method = headers.get(Listener.METAINFO_HTTP_METHOD);
        if (userId != null)
            user = UserGroupCache.getUser(userId);

        List roles = getRoles(path, method);
        if (roles != null && !roles.isEmpty()) {
            if (user == null) {
                throw new AuthorizationException(HTTP_401_UNAUTHORIZED, "Service "
                        + getClass().getSimpleName() + " requires authenticated user");
            }
            for (String role : roles) {
                if (user.hasRole(role)) {
                    return user;
                }
            }
            throw new AuthorizationException(HTTP_401_UNAUTHORIZED,
                    "User: " + userId + " not authorized for: " + path);
        }
        return null;
    }

    /**
     * Default impl pays no attention to method (for compatibility).
     * GET access if restricted if the "Runtime View" role exists.
     */
    protected List getRoles(String path, String method) {
        if (method.equals("GET")) {
            List roles = new ArrayList<>();
            // restrict if view access role exists
            if (UserGroupCache.getRole(Role.RUNTIME_VIEW) != null) {
                roles.add(Role.RUNTIME_VIEW);
                roles.add(Role.PROCESS_EXECUTION);
                roles.add(Workgroup.SITE_ADMIN_GROUP);
            }
            return roles;
        }
        return getRoles(path);
    }

    /**
     * A user who belongs to any role in the returned list is allowed
     * to perform this action/method.  Override to open access beyond Site Admin.
     * @param path
     * @return list of role names or null if authorization not required
     */
    protected List getRoles(String path) {
        List defaultRoles = new ArrayList<>();
        defaultRoles.add(Workgroup.SITE_ADMIN_GROUP);
        return defaultRoles;
    }

    protected String getRequiredProperty(String propName) throws ServiceException {
        String value = PropertyManager.getProperty(propName);
        if (value == null)
            throw new ServiceException(HTTP_404_NOT_FOUND, "Missing property: " + propName);
        return value;
    }

    /**
     * Includes query string.
     */
    protected URL getRequestUrl(Map headers) throws ServiceException {
        String requestUrl = headers.get(Listener.METAINFO_REQUEST_URL);
        if (requestUrl == null)
            throw new ServiceException("Missing header: " + Listener.METAINFO_REQUEST_URL);
        String queryStr = "";
        if (!StringUtils.isBlank(headers.get(Listener.METAINFO_REQUEST_QUERY_STRING)))
            queryStr = "?" + headers.get(Listener.METAINFO_REQUEST_QUERY_STRING);
        try {
            return new URL(requestUrl + queryStr);
        }
        catch (MalformedURLException ex) {
            throw new ServiceException(ServiceException.INTERNAL_ERROR, ex.getMessage(), ex);
        }
    }

    protected void auditLog(UserAction userAction) {
        try {
            ServiceLocator.getUserServices().auditLog(userAction);
        }
        catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
        }
    }

    /**
     * For audit logging.
     */
    protected UserAction getUserAction(User user, String path, Object content, Map headers) {
        Action action = getAction(path, content, headers);
        Entity entity = getEntity(path, content, headers);
        Long entityId = getEntityId(path, content, headers);
        String descrip = getEntityDescription(path, content, headers);
        if (descrip.length() > 1000)
            descrip = descrip.substring(0, 999);
        UserAction userAction = new UserAction(user.getCuid(), action, entity, entityId, descrip);
        userAction.setSource(getSource());
        return userAction;
    }

    protected String getSource() {
        return getClass().getSimpleName() + " Action Service";
    }

    /**
     * Override if entity has a meaningful ID.
     */
    protected Long getEntityId(String path, Object content, Map headers) {
        return 0L;
    }

    protected String getEntityDescription(String path, Object content, Map headers) {
        return path;
    }

    protected Action getAction(String path, Object content, Map headers) {
        String method = headers.get(Listener.METAINFO_HTTP_METHOD);
        if ("POST".equals(method))
            return Action.Create;
        else if ("PUT".equals(method))
            return Action.Change;
        else if ("DELETE".equals(method))
            return Action.Delete;
        else
            return Action.Other;
    }

    protected Map getParameters(Map headers) {
        Map params = new HashMap();
        String query = headers.get(Listener.METAINFO_REQUEST_QUERY_STRING);
        if (query == null)
            query = headers.get("request-query-string");
        if (query != null && !query.isEmpty()) {
            for (String pair : query.split("&")) {
                int idx = pair.indexOf("=");
                try {
                    params.put(URLDecoder.decode(pair.substring(0, idx), "UTF-8"), URLDecoder.decode(pair.substring(idx + 1), "UTF-8"));
                }
                catch (UnsupportedEncodingException ex) { // as if UTF-8 is going to be unsupported
                    logger.error(ex.getMessage(), ex);
                }
            }
        }
        return params;
    }

    protected Query getQuery(String path, Map headers) {
        return new Query(path, getParameters(headers));
    }

    protected String getSegment(String path, int segment) {
        String[] segments = getSegments(path);
        if (segments.length < segment + 1)
            return null;
        else
            return segments[segment];
    }

    /**
     * Minus the base path that triggered this service.
     */
    protected String getSub(String path) {
        int slash = path.indexOf('/');
        if (slash > 0 && slash < path.length() - 1)  // the first part of the path is what got us here
            return path.substring(slash + 1);
        else
            return null;
    }

    protected String[] getSegments(String path) {
        return path.split("/");
    }

    /**
     * Should be overridden.  Avoid using Entity.Other.
     */
    protected Entity getEntity(String path, Object content, Map headers) {
        return Entity.Other;
    }

    protected abstract void validateResponse(String response) throws ServiceException;

    /**
     * returns authenticated user cuid
     */
    protected String getAuthUser(Map headers) {
        return headers.get(Listener.AUTHENTICATED_USER_HEADER);
    }

    /**
     * Also audit logs.
     */
    protected void authorizeExport(Map headers) throws AuthorizationException {
        String path = headers.get(Listener.METAINFO_REQUEST_PATH);
        User user = authorize(path, new JsonObject(), headers);
        Action action = Action.Export;
        Entity entity = getEntity(path, null, headers);
        Long entityId = new Long(0);
        String descrip = path;
        if (descrip.length() > 1000)
            descrip = descrip.substring(0, 999);
        UserAction exportAction = new UserAction(user == null ? "unknown" : user.getName(), action, entity, entityId, descrip);
        exportAction.setSource(getSource());
        auditLog(exportAction);
    }

    /**
     * If you have a unique id that's meaningful (e.g.: order number), it's recommended that
     * you use that for requestId.  However we can generate one if you'd like.
     */
    protected String generateRequestId() {
        return Long.toHexString(System.nanoTime());
    }

    /**
     * Finds masterRequestId from 'mdw-request-id' header, or generates if not found.
     */
    protected String masterRequestId(Map headers) {
        String masterRequestId = headers.get(Listener.METAINFO_MDW_REQUEST_ID);
        if (masterRequestId == null)
            masterRequestId = generateRequestId();
        return masterRequestId;
    }

    protected String logtag(Map headers) {
        return headers.get(Listener.METAINFO_HTTP_METHOD) + " " + headers.get(Listener.METAINFO_REQUEST_PATH);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy