504 Part IV: Applied Security for Oracle APEX and Oracle Business Intelligence logical SQL to see what can be satisfied via cache. Oracle BI uses a shared cache mechanism to increase performance. (The security implications of this cache are discussed in detail in Chapter 14.) If the cache cannot be used to satisfy the request, the BI server will use the metadata defined in the RPD to construct a physical SQL statement (in the case of a relational database source) to be issued to the underlying data sources. NOTE Oracle BI does support a variety of data sources, including XML, relational databases, and several multidimensional databases. It also supports multiple data sources in a single request. For simplicity, the examples provided with this book will often refer to data sources as “the database,” even though it could be much more than just a database. Connection Pools As mentioned, the BI server can be used to query a variety of backend data sources. To set up communication between the BI server and these data sources, you use connection pools in the Administration tool. The name “connection pools” can sometimes be misleading. Sometimes these connections really act like connection pools, a cache of database connections. At other times, these connection pools represent a location of data. To help you understand this, let’s look at several examples. Connection Pools by Data Source Type Connection pools are set up for different types of data sources. Consider, for example, relational databases. The first thing you need to do is decide on a call interface. For Oracle databases, you should always use Oracle Call Interface (OCI). Next, you decide on the maximum number of connections that this pool should allow. The BI server uses the connection pools in the order in which they are listed in the Administration tool. When you attempt to query a data source, the BI server will use the first connection pool that you have permission to access and that has an open connection available. As shown in Figure 13-1, you must configure several options when setting up a connection pool. The documentation covers these in detail, so we will mention only a few of them. The Enable Connection Pooling checkbox in the middle of the dialog determines whether this connection pool will really act like a connection pool. The Require Fully Qualified Table Names checkbox is important when setting up a subject area that accesses multiple database schemas. Depending on how you import the metadata into the administration tool, these schemas might actually end up in different databases (meaning different databases in the Administration tool). You most likely do not want that. If your two schemas end up in two databases in the Administration tool, this will force the BI server to use two connections to access the data, and any joins between objects in these schemas will occur at the BI server level. This could really hamper performance. To make the join happen in the database, the data from both schemas needs to be accessed via a single connection. Each schema should be listed under a single database in the Administration tool, the connection pool should connect the database using a user that has permissions to access both schemas, and the Require Fully Qualified Table Names checkbox should be selected. Finally, the Connection Scripts tab is where you run scripts on connections, before or after queries, or on disconnect. This will be very important in Chapter 14. Chapter 13: Securing Access to Oracle BI 505 Connections to multidimensional data sources are accomplished using XML for Analysis (XMLA). The definition of your connection pool will look quite different. You will use a URL to the XMLA provider for your source. The Use Sessions option in the Connection Pool dialog determines whether a shared connection to the multidimensional data source should be used for all connections. For XML data sources, the connection pool is merely a pointer to the physical location of the XML data source. With XML data sources, you will notice that the XML tab in the connection pool definition dialog is now available and contains settings specific to XML data sources. These settings include timeout settings and the ability to specify XPath or Extensible Stylesheet Language Transformations (XLST) transformations. Connection Pool by Function The documentation recommends that you create connection pools for specific functions. In particular, it lists out the following scenarios: Data access Authentication ■ ■ FIGURE 13-1 Connection pool setup 506 Part IV: Applied Security for Oracle APEX and Oracle Business Intelligence Authorization Populating session variables Populating repository variables Although many of these topics have not yet been discussed, you should understand that you will want to use separate connection pools for each of these scenarios for two reasons: First, dedicated connection pools for each scenario help to control performance. The resources used to log a user onto Oracle BI will be separated from the resources used by users actually querying databases. As you will see, many things can happen while a user’s session is being established. You’ll want to take care to make that logon as fast as possible. Second, you may want to run different connection pool scripts in different scenarios. For example, in Chapter 14, we use connection pool scripts to persist the end user’s identity to the database. Often, this needs to happen only during actual data access. Multiple Connection Pools We just discussed one reason for multiple connection pools: different connection pools based on what that connection pool is doing. You might also want to create multiple connection pools for data access. This allows you to control the maximum number of connections per pool, and it allows you to perform resource management. Suppose, for example, that you wanted to give one group of users more database resources than another group. You can create two connection pools and set them up to use different shared logons. On the database, it is now easy to isolate each group of users and assign the appropriate resources. In Oracle BI, you can control which group of users uses which connection pool by setting permissions on the connection pool. For example, group A has permission to use connection pool A, and the database applies a certain resource policy to those connections. Group B has permissions to use connection pool B, and the database can apply a different resource policy to these users. It is worth summarizing how Oracle BI decides which connection pool to use. For everything but data access, you will be able to state explicitly which connection pool you want to use. For example, in the upcoming sections, we will use a feature called “initialization blocks” to set up authentication. In the initialization block, you get to specify the exact connection pool you want to use. For data access, you do not get to specify the connection pool you use. When you define connection pools for a database in the Administration tool, the order is important. For data access, the BI server uses the first connection pool that you have permission to use and that has open connections available. TIP Make sure your data access connection pools are always listed first in the list of connection pools. Variables As mentioned, understanding session variables is a crucial part of securing Oracle BI. Setting up session variables is in fact a prerequisite step to securing Oracle BI. In this section, we will cover all types of Oracle BI variables: server, presentation, and session variables. ■ ■ ■ ■ ■ Chapter 13: Securing Access to Oracle BI 507 Server and Presentation Variables Server variables are either static variables or dynamic variables, and they work as their names imply. Static variables are set when the BI server starts, and dynamic variables are refreshed on a periodic basis. The examples used in this book and the metadata that ships with the Oracle BI (SampleSales.rpd) use a static server variable called BI_EE_HOME. This holds the value of where you installed Oracle BI and is used in the connection pool for the Sample Sales database to point to the XML data source. (You’ll notice in the appendix that you will need to set this variable before the examples can be used.) An example of a dynamic server variable is also included in the examples for this book. This variable in the example is called CURRENT_YEAR. The value of this variable is set to be refreshed every night at midnight (although it will change only once per year). This variable could be used to create a rolling window filter, and it makes it extremely easy for you to create a report that always shows the current year. Server variables are managed in the BI server metadata using the Administrator tool. Use the Manage Variables screen to create these variables. An initialization block is used to define how the variable is populated and the refresh frequency. Figure 13-2 shows an example server variable that stores the current year. FIGURE 13-2 Server variables 508 Part IV: Applied Security for Oracle APEX and Oracle Business Intelligence Presentation variables are defined and used in the dashboard. These are great to use for such things as allowing the user to set user-defined thresholds on the fly. However, they are not normally used for security purposes and not discussed in detail in this book. Session Variables That leaves us with session variables, which are extremely useful for security purposes. They get populated as a session is created. Initialization blocks are used to populate session variables. Initialization blocks have three parts: the data source, the variable target, and the execution precedence. The data source can be a database, LDAP, or a custom authenticator. If the data you need to establish a session is in a database, you will use a standard SQL query to retrieve that information. If your data is in an LDAP data source, you just specify the LDAP attributes that need to be retrieved by the initialization block. Custom authenticators are actually OS-level programs that you write to obtain session information. Examples of each type of data source will be used in the upcoming section “Authentication and Authorization.” The variable target defines where the initialization block will put the data that it retrieves. A single initialization block can populate one or more variable, each with one or more values. If the initialization block returns single values for each variable, then a simple mapping of column or attribute to variable name is done in the variable target section of the initialization block definition. If the initialization block returns more than one value for a variable, then a feature called “row-wise initialization” is used to populate the variable. (This is discussed in full detail with an example in the next section.) The third and final part of the initialization block is the execution precedence. The execution precedence determines the order in which initialization blocks are run. This is very important if the value returned in one initialization block is to be used as a parameter in a second initialization block and is extremely common. For example, it is common to first populate the USER session variable. After you know who the user is, you can look up other session information such as group membership. For this case, it is important that the initialization block that populates the USER session variable runs before the initialization block that populates group membership. Row-wise Initialization As mentioned, it is possible to return more than one value into a variable. For example, if you are using initialization blocks to assign group membership dynamically (discussed in more detail shortly), a user may belong to more than one group. Each group to which the user belongs will be assigned to the GROUP variable separated by semicolons. To return more than one value to a variable, you mark the variable target as row-wise initialization. Row-wise initialization actually does two things: It creates one or more variables, and it assigns one or more values to each variable that it created. Row-wise initialization works only for variables that use databases as a data source. This fact will be important when we look at dynamic group membership and LDAP authentication. In an example RPD provided with this book, you will find an initialization block called get_ products. This initialization block populates a variable called PRODUCT with the products that a given product manager is allowed to see. Here’s the data source: SELECT 'PRODUCT' variable_name, product variable_value FROM bi_tables.product_managers WHERE user_name = ':USER' Chapter 13: Securing Access to Oracle BI 509 You’ll notice a couple of things about this SQL statement: When using row-wise initialization, the query must return the variable name in the first column and the value of the variable in the second column. Essentially, you end up with an array of name-value pairs. You should also notice the use of the bind variable :USER. This is specifically referring to the USER session variable. Running the query for the user biproduct2 yields two rows: one for each product that this user manages. Figure 13-3 shows these results. The BI server will take the results of this query and populate the PRODUCT session variable with the value Portable PCs; Desktop PCs. Notice how the multirow result set is automatically transformed into the semicolon-delimited list. If the first column had contained more than one value, then one session variable would be created for each distinct value returned in the first column of the query. For example, if you wanted to populate the CHANNEL and PRODUCT variables in a single initialization block, your SQL might look something like this: SELECT 'PRODUCT' variable_name, product variable_value FROM bi_tables.product_managers WHERE user_name = 'biproduct2' UNION ALL SELECT 'CHANNEL' variable_name, channel_desc variable_value FROM sh.channels In the next section, we use session variables to establish group membership. This will require that you use session variables in the BI server metadata. It is also possible to use session variables in reports and dashboards. We will use this to verify that our session variables are working as designed. In Chapter 14, we will use session variable values to implement business model filters and to make Oracle BI work with the Oracle Virtual Private Database feature. FIGURE 13-3 Row-wise initialization query 510 Part IV: Applied Security for Oracle APEX and Oracle Business Intelligence Authentication and Authorization Several methods of authentication are available in Oracle BI. Here, we focus on the ways you can externalize authentication; hopefully, enabling you to reuse an existing identity management infrastructure. After detailing how these different methods of external authentication work, we focus on how to handle group membership when using external identity stores. Authentication Options You can authenticate with Oracle BI in six ways: built-in, LDAP, database table, database authentication using RPD and database users, database authentication using only database users, and custom authentication. We will explore all of these methods of authentication in this section. Built-in Authentication When using built-in authentication, users and groups are defined in the RPD metadata. This method is great for demonstration purposes and test systems but should not be considered for production systems. It lacks many of the capabilities required for strong password policies such as support for password complexity rules, password expirations, self-service password resets, and delegated administration. Another reason for avoiding built-in authentication is the complexity of the provisioning process. In the “Types of Groups” section, we cover the concepts of groups and web groups. When you provision a user, the user must be properly assigned both to groups and web groups. When using built-in authentication, you cannot assign a user to a web group until that user has logged on to the system. This means that the user must logon to Oracle BI in the middle of the provisioning process and before authorization has been completely set up. Database Authentication Using RPD and Database Users There are two ways to set up database authentication: using RPD and database users or using only database users. The first method is described in this section, and detailed setup steps are included in the documentation. The second method is detailed in the “External Authentication Methods” section. Database authentication using RPD and database users allows end users to use their existing database usernames and passwords to authenticate to Oracle BI. This authentication method requires that the user account exist in both the RPD and the database. At the database, the user account must be completely setup including a valid password. In the RPD, an account with the same username must be created, but the password should not be set. The validation of the username and password will happen at the database. At first glance, this might not seem much better than built-in authentication, because you still have to create users in the RPD. However, as the passwords need not be stored in the RPD, it does provide advantages over built-in authentication. Password management, including expirations, resetting of passwords, and password complexity, can be handled external to Oracle BI. The database username and password being used to authenticate to Oracle BI are not necessarily the same credentials that will be used to access the data in the backend databases. This username and password are used only to establish an Oracle BI session. Connections from the BI server to the backend databases use the credentials defined in the data access connection pool for the respective databases. External Authentication Methods The next four methods of authentication are all similar and are referred to as “external authentication.” One might argue that the section “Database Authentication Using RPD and Database Users” should be included in this category. I exclude it for few reasons. First, because the users must still Chapter 13: Securing Access to Oracle BI 511 be created in the RPD, database authentication does not completely externalize authentication. Second, it does not follow the pattern of authentication you will see in the next four sections. Finally, using the pattern of authentication outlined in the following discussion, you’ll find it possible to revisit database authentication using database users only and set it up in such a way that usernames are not stored in the RPD. For external authentication to work, two things must happen: The username and password must be validated, and the USER session variable must be populated with the BI user’s username. Remember that our discussion focuses on authentication. To provision a user properly, you must also establish authorization, which is discussed later in the section “Authorization.” Authorization is covered in a separate section for two reasons: for clarity and organization, and because your method of authentication does not dictate your method of authorization. You may, for example, choose to use LDAP authentication and table-based authorization. Table-based Authentication Table-based authentication requires that a list of usernames and passwords be stored in a table in a database. In this section, we are again using a database for authentication. However, this time we are not using database accounts for authentication. Instead, we are using the database to store usernames and password in a table. It is always a good idea to hash stored passwords. The Oracle BI documentation gives an example of setting up table based authentication with plaintext passwords. The example RPD aos_table.rpd provided with this book follows this method but includes a method to hash the passwords. Notice that the hashing process occurs on the database and the password will be passed over the network in plaintext. It is recommended that you encrypt communication between the BI server and the backend database (for example, by using the advanced security option for Oracle). The first step in setting up table-based authentication is to create the table required to hold the usernames and passwords. An example schema with the necessary tables is included with this book, and the appendix details the specifics of this schema. Here’s a function to simplify the hashing of the passwords that uses the DBMS_CRYPTO package of the database: CREATE OR REPLACE FUNCTION hash_password ( p_password IN VARCHAR2) RETURN VARCHAR2 AS v_hash_algorithm pls_integer; v_return VARCHAR2(4000); BEGIN v_hash_algorithm := dbms_crypto.hash_sh1; SELECT dbms_crypto.hash(to_clob(p_password), v_hash_algorithm) INTO v_return FROM dual; RETURN v_return; END; Next, I created an initialization block to verify the username and password. Here is the code that the initialization block executes: SELECT user_name FROM bi_security_tables.bi_users WHERE upper(':USER')= upper(user_name) NQS_PASSWORD_CLAUSE(AND hash_password(':PASSWORD') = user_password)NQS_PASSWORD_CLAUSE 512 Part IV: Applied Security for Oracle APEX and Oracle Business Intelligence Notice that row-wise initialization is not used here because the query is expected to return a single value. In this case, the value of the USER session variable is returned and the variable target section of the initialization block is used to specify that the result of the query be assigned to the USER session variable. The other important thing to notice about this query is the use of NQS_PASSWORD_CLAUSE. To help you understand why this is important, you need to understand the notion of “proxy authentication.” You can log onto the BI server as one user and tell the BI server to run everything as another user. This technique is used in SSO and is discussed later in the chapter. Oracle BI also uses this feature for interprocess communication. For example, if the scheduler service needs to run a scheduled job for a user called BIUSER, it will log onto the BI server as the user Administrator and tell the BI server to run everything as BIUSER. Oracle BI Publisher also uses proxy authentication when communicating with the BI server. When the BI server gets a proxy authentication request, it first authenticates the proxy user, Administrator in this example. Then it runs all the initialization blocks for the target user, BIUSER in this example. This is exactly what you want to happen, because then you know that BIUSER’s session is properly set up. The only problem with this is that the scheduler service does not know BIUSER’s password. The authentication initialization block would fail, then, because it requires both the username and password to be correct. NQS_PASSWORD_CLAUSE fixes this problem. When the BI server is performing a proxy authentication, it strips the NQS_PASSWORD_CLAUSE out of the authentication initialization block. LDAP Authentication The Oracle BI documentation mentions two ways that an LDAP server can be used. It discusses importing LDAP users and groups into Oracle BI and using LDAP as an external authentication method. The import capability is really just a quick way to load users into the RPD and actually uses internal authentication. This should be used only for testing purposes and should not be confused with LDAP authentication. If you already have an LDAP set up, you should not import the users from LDAP, but use the LDAP server as an external authentication method instead. As with external table authentication, two basic steps are required to perform LDAP authentication, as shown in the next illustration. First, the BI server must verify that the user’s password is correct. Then it assigns a value to the USER session variable. When setting up LDAP authentication, you first define an LDAP server in your BI server metadata, the RPD. This is done in the Security Manager of the Administration tool. Figure 13-4 shows the process of setting up an LDAP server. Chapter 13: Securing Access to Oracle BI 513 If your LDAP server accepts anonymous binds, you can leave the Bind DN and Bind Password fields blank. On the Advanced tab is an option to use SSL connections to the LDAP server. Use of SSL is highly recommended, as passwords are passed over the network in plaintext when using LDAP authentication. Next, create an initialization block to populate the USER session variable. This initialization block will perform the LDAP bind to test the validity of the username and password and can return any LDAP attribute. At a minimum, the username (uid) needs to be returned and placed in the USER session variable. Figure 13-5 shows a completed initialization block for LDAP authentication. At this point, you could try logging into Oracle BI using your LDAP credentials. It is important that you remember that authorization still needs to be set up (and is covered in the appropriate section a bit later in the chapter). FIGURE 13-4 LDAP server setup . filters and to make Oracle BI work with the Oracle Virtual Private Database feature. FIGURE 13-3 Row-wise initialization query 510 Part IV: Applied Security for Oracle APEX and Oracle Business Intelligence Authentication. to Oracle BI in the middle of the provisioning process and before authorization has been completely set up. Database Authentication Using RPD and Database Users There are two ways to set up database. Authentication Methods” section. Database authentication using RPD and database users allows end users to use their existing database usernames and passwords to authenticate to Oracle BI. This authentication