Here are some notes on the way I set an instance of RequestTracker up to support the following workflow:
- Customers use a web form which sends an email to RT.
- A contact person receives all new tickets by email and uses the RT web UI to allocate it to a another person.
- The person handling the ticket gets a copy of the ticket (and doesn't see any of the other tickets) via email and never has to use RT.
Groups and Queues
For each product, I created the following RT groups:
productname-contact
: Users receiving all emails sent to the ProductName queuesproductname-support
: People who can be assigned to ProductName queues
as well as a ProductName
queue:
productname-contact
is a watcherproductname-contact
has all of the permissions under "Group Rights"productname-support
has all of the permissions except for AdminCC under "Group Rights"
User accounts
The contact person (can be more than one person), responsible for allocating tickets to support people gets a full RT account with a password and is a member of the productname-contact
group.
The support people, who reply to tickets, get a limited RT account without a password (so they cannot login) and are members of the productname-support
group.
Forwarding the initial email
In a stock RT install, this workflow works well, except for one important point: when a ticket is allocated to someone else, all they get is a "this ticket has been assigned to you" email. They have no idea what the ticket is about and since they can't login into RT and are not CCed on the emails going into that queue, that's pretty useless to them.
The solution I found is to add a new ForwardFirstMessage global template:
{
my $Transactions = $Ticket->Transactions;
$Transactions->Limit( FIELD => 'Type', VALUE => 'Create' );
my $first_message;
my $CreateObj = $Transactions->First;
if( $CreateObj && $CreateObj->id ) {
$first_message = $CreateObj->Content;
}
$first_message;
}
and then edit the On Owner Change Notify Owner
global scrip to make use of that template.
With this, the person that is allocated to a ticket will receive the original email from the requestor as if it had been sent directly to their email address in the first place.
Content Security Policy is a proposed HTTP extension which allows websites to restrict the external content that can be displayed by visiting web browsers. By expressing a set of rules to be enforced by the browser, a website is able to prevent the injection of outside resources by malicious users.
While adding support for the March 2011 draft in Libravatar, I looked at three different approaches.
Controlling the headers in the application
The first approach I considered was to have the Django application output all of the headers, which is what the django-csp module does. Unfortunately, I need to be able to vary the policy between pages (the views in Libravatar have different requirements) and that's one of the things that hasn't been implemented yet in that module.
Producing the same headers by hand is fairly simple:
response = render_to_response('app/view.html')
response['X-Content-Security-Policy'] = "allow 'self'"
return response
but it would mean adding a bit of code to every view and/or writing a custom wrapper for render_to_response()
.
Setting a default header in Apache
Ideally, I'd like to be able to set a default header in Apache using mod_headers and then override it as needed inside the application.
The first problem with this solution is that it's not possible (as far I can tell) for a Django application to override a header set by Apache:
- mod_headers adds its response header after mod_wsgi has returned (unless early processing is used).
- Django's response objects cannot see the early headers set by Apache.
The second problem is that mod_headers doesn't have an action that adds/sets a header only if it didn't already exist. It does have append
and merge
actions which could in theory be used to add extra terms to the policy but it unfortunately uses a different separator (the comma) from the CSP spec (which uses semi-colons).
Always set headers in Apache
While I would have liked to get the second approach working, in the end, I included all of the CSP directives within the main Apache config file:
Header set X-Content-Security-Policy: "allow 'self'; options inline-script; img-src 'self' data:"
<Location /account/confirm_email>
Header set X-Content-Security-Policy: "allow 'self'; options inline-script; img-src *"
</Location>
<Location /tools/check>
Header set X-Content-Security-Policy: "allow 'self'; options inline-script; img-src *"
</Location>
The first Header
call sets a default policy which is later overriden based on the path to the Django view that's being used.
Related technologies
If you are interested in Content Security Policy, you may also want to look into Application Boundaries Enforcer (part of the NoScript Firefox extension) for more security rules that can be supplied by the server and enforced client-side.
It's also worth mentioning the excellent Request Policy extension which solves the same problem by letting users whitelist the cross-site requests they want to allow.