/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.apache.ranger.plugin.store.file; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ranger.plugin.geo.GeolocationMetadata; import org.apache.ranger.plugin.store.GeolocationStore; import org.apache.ranger.plugin.geo.RangerGeolocationDatabase; import org.apache.ranger.plugin.geo.RangerGeolocationData; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.util.HashMap; import java.util.Map; public class GeolocationFileStore implements GeolocationStore { private static final Log LOG = LogFactory.getLog(GeolocationFileStore.class); public static final String GeoLineCommentIdentifier = "#"; public static final Character GeoFieldsSeparator = ','; public static final String PROP_GEOLOCATION_FILE_LOCATION = "FilePath"; public static final String PROP_GEOLOCATION_FILE_REINIT = "ForceRead"; public static final String PROP_GEOLOCATION_IP_IN_DOT_FORMAT = "IPInDotFormat"; private static Map<String, RangerGeolocationDatabase> geolocationDBMap = new HashMap<>(); private RangerGeolocationDatabase geolocationDatabase; private boolean isMetalineProcessed; private boolean useDotFormat; @Override public void init(final Map<String, String> context) { String filePathToGeolocationFile = context.get(PROP_GEOLOCATION_FILE_LOCATION); if (StringUtils.isBlank(filePathToGeolocationFile)) { filePathToGeolocationFile = "/etc/ranger/data/geo.txt"; } String reinit = context.get(PROP_GEOLOCATION_FILE_REINIT); boolean reinitialize = reinit == null || Boolean.parseBoolean(reinit); String ipInDotFormat = context.get(PROP_GEOLOCATION_IP_IN_DOT_FORMAT); useDotFormat = ipInDotFormat == null || Boolean.parseBoolean(ipInDotFormat); if (LOG.isDebugEnabled()) { LOG.debug("GeolocationFileStore.init() - Geolocation file location=" + filePathToGeolocationFile); LOG.debug("GeolocationFileStore.init() - Reinitialize flag =" + reinitialize); LOG.debug("GeolocationFileStore.init() - UseDotFormat flag =" + useDotFormat); } RangerGeolocationDatabase database = geolocationDBMap.get(filePathToGeolocationFile); if (database == null || reinitialize) { RangerGeolocationDatabase newDatabase = build(filePathToGeolocationFile); if (newDatabase != null) { geolocationDBMap.put(filePathToGeolocationFile, newDatabase); database = newDatabase; } else { LOG.error("GeolocationFileStore.init() - Could not build database. Using old database if present."); } } geolocationDatabase = database; if (geolocationDatabase == null) { LOG.error("GeolocationFileStore.init() - Cannot build Geolocation database from file " + filePathToGeolocationFile); } } @Override public RangerGeolocationDatabase getGeoDatabase() { return geolocationDatabase; } @Override public final RangerGeolocationData getGeoLocation(final String ipAddress) { RangerGeolocationData ret = null; RangerGeolocationDatabase database = geolocationDatabase; // init() may get called when getGeolocation is half-executed if (database != null) { long start = 0L, end = 0L; start = System.currentTimeMillis(); ret = database.find(ipAddress); end = System.currentTimeMillis(); if (LOG.isDebugEnabled()) { if (ret == null) { LOG.debug("GeolocationFileStore.getGeolocation() - " + ipAddress + " not found. Search time = " + (end - start) + " milliseconds"); } else { LOG.debug("GeolocationFileStore.getGeolocation() - " + ipAddress + " found. Search time = " + (end - start) + " milliseconds"); for (String attrName : database.getMetadata().getLocationDataItemNames()) { LOG.debug("GeolocationFileStore.getGeolocation() - IPAddress[" + attrName + "]=" + database.getValue(ret, attrName) + ", "); } } } } else { LOG.error("GeolocationFileStore.getGeolocation() - GeoLocationDatabase is not initialized correctly."); } return ret; } private Reader getReader(String dataFileName) throws IOException { Reader ret = null; File f = new File(dataFileName); if(f.exists() && f.canRead()) { LOG.info("GeolocationFileStore: reading location data from file '" + dataFileName + "'"); ret = new FileReader(dataFileName); } else { InputStream inStr = this.getClass().getResourceAsStream(dataFileName); if(inStr != null) { LOG.info("GeolocationFileStore: reading location data from resource '" + dataFileName + "'"); ret = new InputStreamReader(inStr); } } if(ret == null) { throw new FileNotFoundException(dataFileName); } return ret; } RangerGeolocationDatabase build(String dataFileName) { RangerGeolocationDatabase database = null; BufferedReader bufferedReader = null; long start = 0L, end = 0L; start = System.currentTimeMillis(); try { bufferedReader = new BufferedReader(getReader(dataFileName)); database = new RangerGeolocationDatabase(); String line; int lineNumber = 0; isMetalineProcessed = false; while(( line = bufferedReader.readLine()) != null) { lineNumber++; if (!processLine(lineNumber, line, database)) { LOG.error("RangerGeolocationDatabaseBuilder.build() - Invalid geo-specification - " + lineNumber + ":" + line); database = null; break; } } bufferedReader.close(); bufferedReader = null; } catch(FileNotFoundException ex) { LOG.error("RangerGeolocationDatabaseBuilder.build() - Unable to open file '" + dataFileName + "'"); } catch(IOException ex) { LOG.error("RangerGeolocationDatabaseBuilder.build() - Error reading file '" + dataFileName + "', " + ex); } finally { if (bufferedReader != null) { try { bufferedReader.close(); } catch (Exception exception) { // Ignore } } } end = System.currentTimeMillis(); if (LOG.isDebugEnabled()) { LOG.debug("RangerGeolocationDatabaseBuilder.build() - Time taken for reading file = " + (end - start) + " milliseconds"); } if (database != null) { database.optimize(); } return database; } private boolean processLine(int lineNumber, String line, RangerGeolocationDatabase database) { boolean ret = true; line = line.trim(); if (!line.startsWith(GeoLineCommentIdentifier)) { String fields[] = StringUtils.split(line, GeoFieldsSeparator); if (fields != null) { if (!isMetalineProcessed) { GeolocationMetadata metadata = GeolocationMetadata.create(fields, lineNumber); if (metadata != null) { database.setMetadata(metadata); isMetalineProcessed = true; } else { LOG.error("GeolocationFileStore.processLine() - Invalid metadata specification " + lineNumber + ":" + line); ret = false; } } else { RangerGeolocationData data = RangerGeolocationData.create(fields, lineNumber, useDotFormat); if (data != null) { database.getData().insert(data); } else { LOG.error("GeolocationFileStore.processLine() - Invalid data specification " + lineNumber + ":" + line); } } } else { LOG.error("GeolocationFileStore.processLine() - Invalid line, skipping.." + lineNumber + ":" + line); } } return ret; } }